Advanced
Events
Handle NuGrid events for user interactions.
NuGrid emits events for various user interactions, allowing you to respond to clicks, edits, navigation, and state changes.
Event Categories
Click Events
| Event | Payload | Description |
|---|---|---|
cell-clicked | NuGridCellClickEvent | Cell was clicked |
cell-double-clicked | NuGridCellClickEvent | Cell was double-clicked |
row-clicked | NuGridRowClickEvent | Row was clicked |
row-double-clicked | NuGridRowClickEvent | Row was double-clicked |
Focus Events
| Event | Payload | Description |
|---|---|---|
focused-cell-changed | NuGridFocusedCellChangedEvent | Focused cell changed |
focused-row-changed | NuGridFocusedRowChangedEvent | Focused row changed |
Keyboard Events
| Event | Payload | Description |
|---|---|---|
keydown | NuGridKeydownEvent | Unhandled keyboard event (for custom shortcuts) |
Editing Events
| Event | Payload | Description |
|---|---|---|
cell-editing-started | NuGridCellEditingStartedEvent | Cell editing began |
cell-editing-cancelled | NuGridCellEditingCancelledEvent | Cell editing was cancelled |
cell-value-changed | NuGridCellValueChangedEvent | Cell value was saved |
State Events
| Event | Payload | Description |
|---|---|---|
sort-changed | NuGridSortChangedEvent | Sorting changed |
filter-changed | NuGridFilterChangedEvent | Filters changed |
row-selection-changed | Record<string, boolean> | Selection changed |
Row Events
| Event | Payload | Description |
|---|---|---|
row-add-requested | Row data | New row submitted |
row-dragged | Drag event | Row was reordered |
Click Events
Cell Click
<script setup lang="ts">
import type { NuGridCellClickEvent } from '#nu-grid/types'
function onCellClicked(event: NuGridCellClickEvent<User>) {
console.log('Cell clicked:', {
row: event.row.original,
column: event.column.id,
value: event.value,
})
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
@cell-clicked="onCellClicked"
/>
</template>
Cell Double Click
<script setup lang="ts">
function onCellDoubleClicked(event) {
// Navigate to detail page on double-click
navigateTo(`/users/${event.row.original.id}`)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
@cell-double-clicked="onCellDoubleClicked"
/>
</template>
Row Click
<script setup lang="ts">
function onRowClicked(event) {
// Select row on click
selectedUserId.value = event.row.original.id
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
@row-clicked="onRowClicked"
/>
</template>
Focus Events
Focused Cell Changed
<script setup lang="ts">
import type { NuGridFocusedCellChangedEvent } from '#nu-grid/types'
function onFocusedCellChanged(event: NuGridFocusedCellChangedEvent<User>) {
console.log('Focus moved:', {
from: `${event.previousRowIndex}:${event.previousColumnIndex}`,
to: `${event.rowIndex}:${event.columnIndex}`,
rowId: event.rowId,
columnId: event.columnId,
})
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:focus="{ mode: 'cell' }"
@focused-cell-changed="onFocusedCellChanged"
/>
</template>
Focused Row Changed
<script setup lang="ts">
function onFocusedRowChanged(event) {
// Load details for focused row
loadUserDetails(event.rowId)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:focus="{ mode: 'row' }"
@focused-row-changed="onFocusedRowChanged"
/>
</template>
Keyboard Events
Custom Keyboard Shortcuts
The keydown event fires before NuGrid's internal handlers process the key. This allows you to intercept keys and prevent NuGrid's default behavior by setting event.handled = true. Use this to implement custom shortcuts like Enter-to-navigate:
<script setup lang="ts">
import type { NuGridKeydownEvent } from '#nu-grid/types'
function onKeydown(event: NuGridKeydownEvent<User>) {
if (!event.rowData) return
// Navigate to detail page on Enter (prevent NuGrid from starting editing)
if (event.event.key === 'Enter') {
event.handled = true // Prevents NuGrid's default Enter behavior
navigateTo(`/users/${event.rowData.id}`)
}
// Delete selected row on Delete key
if (event.event.key === 'Delete') {
event.handled = true
deleteUser(event.rowData.id)
}
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:focus="{ mode: 'row' }"
@keydown="onKeydown"
/>
</template>
Keydown Event Payload
The NuGridKeydownEvent provides context about the focused cell/row:
| Property | Type | Description |
|---|---|---|
event | KeyboardEvent | The original keyboard event |
row | Row<T> | null | Currently focused row |
rowData | T | null | The original row data (convenience for row.original) |
rowId | string | null | Current row ID |
rowIndex | number | Current row index (-1 if none) |
column | Column<T> | null | Currently focused column (cell mode only) |
columnId | string | null | Current column ID |
columnName | string | null | The column header name |
columnIndex | number | Current column index (-1 if none) |
cell | Cell<T> | null | Currently focused cell (cell mode only) |
value | unknown | The current cell value |
handled | boolean | Set to true to prevent NuGrid's internal handling |
Example: Quick Actions
<script setup lang="ts">
function onKeydown(event: NuGridKeydownEvent<Task>) {
const task = event.rowData
if (!task) return
switch (event.event.key) {
case 'Enter':
// Open task details instead of editing
event.handled = true
openTaskModal(task.id)
break
case 'e':
// Quick edit (only if no modifier keys)
if (!event.event.ctrlKey && !event.event.metaKey) {
event.handled = true
startEditing(task.id)
}
break
case 'c':
// Mark complete (only if no modifier keys)
if (!event.event.ctrlKey && !event.event.metaKey) {
event.handled = true
toggleComplete(task.id)
}
break
case 'Delete':
case 'Backspace':
// Delete with confirmation
event.handled = true
confirmDelete(task.id)
break
}
}
</script>
<template>
<NuGrid
:data="tasks"
:columns="columns"
:focus="{ mode: 'row', autoFocus: true }"
@keydown="onKeydown"
/>
</template>
Editing Events
Cell Editing Started
<script setup lang="ts">
function onCellEditingStarted(event) {
console.log('Started editing:', event.column.id, 'value:', event.value)
// Prevent editing for locked rows
if (event.row.original.locked) {
return false // Cancels the edit
}
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-editing-started="onCellEditingStarted"
/>
</template>
Cell Value Changed
<script setup lang="ts">
async function onCellValueChanged(event) {
console.log('Value changed:', {
column: event.column.id,
oldValue: event.oldValue,
newValue: event.newValue,
})
// Persist to server
try {
await api.updateUser(event.row.original.id, {
[event.column.id]: event.newValue,
})
toast.add({ title: 'Saved', color: 'success' })
} catch (error) {
toast.add({ title: 'Failed to save', color: 'error' })
// Optionally revert
event.row.original[event.column.id] = event.oldValue
}
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-value-changed="onCellValueChanged"
/>
</template>
Cell Editing Cancelled
<script setup lang="ts">
function onCellEditingCancelled(event) {
console.log('Editing cancelled:', event.column.id)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
@cell-editing-cancelled="onCellEditingCancelled"
/>
</template>
State Events
Sort Changed
<script setup lang="ts">
function onSortChanged(event) {
console.log('Sorting:', event.sorting)
// [{ id: 'name', desc: false }, ...]
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
@sort-changed="onSortChanged"
/>
</template>
Filter Changed
<script setup lang="ts">
function onFilterChanged(event) {
console.log('Filters:', event.columnFilters)
// [{ id: 'status', value: 'active' }, ...]
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
@filter-changed="onFilterChanged"
/>
</template>
Row Selection Changed
<script setup lang="ts">
function onRowSelectionChanged(selection: Record<string, boolean>) {
const selectedIds = Object.entries(selection)
.filter(([, selected]) => selected)
.map(([id]) => id)
console.log('Selected:', selectedIds)
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
row-selection="multi"
@row-selection-changed="onRowSelectionChanged"
/>
</template>
Example: Event Logger
<script setup lang="ts">
interface LogEntry {
id: number
event: string
time: string
payload: string
}
const log = ref<LogEntry[]>([])
let counter = 0
function logEvent(name: string, payload: any) {
log.value.unshift({
id: ++counter,
event: name,
time: new Date().toLocaleTimeString(),
payload: JSON.stringify(payload, null, 2),
})
if (log.value.length > 50) {
log.value.pop()
}
}
</script>
<template>
<div class="flex gap-4">
<NuGrid
:data="data"
:columns="columns"
:editing="{ enabled: true }"
:focus="{ mode: 'cell' }"
row-selection="multi"
class="flex-1"
@cell-clicked="e => logEvent('cell-clicked', { row: e.row.id, col: e.column.id })"
@cell-double-clicked="e => logEvent('cell-double-clicked', { row: e.row.id, col: e.column.id })"
@focused-cell-changed="e => logEvent('focused-cell-changed', { row: e.rowIndex, col: e.columnIndex })"
@cell-editing-started="e => logEvent('cell-editing-started', { col: e.column.id })"
@cell-value-changed="e => logEvent('cell-value-changed', { old: e.oldValue, new: e.newValue })"
@sort-changed="e => logEvent('sort-changed', e.sorting)"
@row-selection-changed="e => logEvent('row-selection-changed', Object.keys(e).length)"
/>
<div class="w-80 rounded-lg border p-4">
<h3 class="mb-2 font-semibold">Event Log</h3>
<div class="max-h-96 space-y-2 overflow-auto text-xs">
<div v-for="entry in log" :key="entry.id" class="rounded bg-gray-50 p-2 dark:bg-gray-800">
<div class="flex justify-between">
<span class="font-medium text-primary">{{ entry.event }}</span>
<span class="text-gray-400">{{ entry.time }}</span>
</div>
<pre class="mt-1 text-gray-600 dark:text-gray-400">{{ entry.payload }}</pre>
</div>
</div>
</div>
</div>
</template>