# useGridRowAction

[← Back to Composables README](https://github.com/NantHealth/featherk/blob/integration/packages/composables/README.md)

Composable that provides safe, accessible row click and keyboard activation handlers for Kendo UI for Vue `Grid` components.

## Features

- **Safe Row Clicks**: Ignores clicks originating on interactive child elements (`button`, `a`, `input`, `.k-checkbox`, kebab menus, etc.).
- **Text Selection Protection**: Automatically suppresses row actions when the user is highlighting/selecting text inside a cell within the row.
- **Primary Mouse Button**: Restricts click activation to primary (left) mouse clicks.
- **Keyboard Accessibility**: Supports keyboard row activation (`Enter` / `Space`) when focus is on a data row.
- **Action Decoupled**: Provides normalized row event context (`dataItem`, `rowIndex`, `field`, `triggerType`, modifier keys, and optional coordinates) while leaving action logic (dialog, route navigation, context menu) to the consumer.
- **Composable Combination**: Integrates seamlessly with `useGridA11y` for combined row navigation and row action handling.

## Prerequisites

- Vue 3 Composition API
- `@progress/kendo-vue-grid`
- `@featherk/composables`

Install:

```bash
npm install @featherk/composables
```

## Quick Start

### Implementation Checklist

1. Import `useGridRowAction` and configure `onRowAction` with your action handler.
2. Bind `@rowclick` on the Kendo Grid to `handleRowClick`.
3. Bind `@keydown` on the Kendo Grid to `handleRowKeyDown`.

```vue
<template>
  <!-- Step 2: Wire @rowclick to handleRowClick -->
  <!-- Step 3: Wire @keydown to handleRowKeyDown -->
  <Grid
    :data-items="gridData"
    :columns="columns"
    @rowclick="handleRowClick"
    @keydown="handleRowKeyDown"
  />
</template>

<script setup lang="ts">
import { ref } from "vue";
import { Grid } from "@progress/kendo-vue-grid";
import { useGridRowAction, type RowActionContext } from "@featherk/composables/grid";

interface Patient {
  id: number;
  name: string;
  status: string;
}

const gridData = ref<Patient[]>([
  { id: 1, name: "Jane Doe", status: "Active" },
  { id: 2, name: "John Smith", status: "Pending" },
]);

const columns = [
  { field: "id", title: "ID", width: "80px" },
  { field: "name", title: "Patient Name" },
  { field: "status", title: "Status" },
];

// Step 1: Initialize useGridRowAction with options and action handler
const { handleRowClick, handleRowKeyDown } = useGridRowAction<Patient>({
  dataItems: gridData,
  onRowAction: (dataItem: Patient, context: RowActionContext<Patient>) => {
    console.log(`Row activated (${context.triggerType}):`, dataItem);
    // Execute consumer action (e.g., open dialog, router.push, etc.)
  },
});
</script>
```

## Integrating with `useGridA11y`

When combining `useGridA11y` (for `ArrowUp`/`ArrowDown` row focus navigation) with `useGridRowAction` (for `Enter`/`Space` row activation), delegate both handlers in a single `@keydown` listener:

```vue
<template>
  <Grid
    ref="gridRef"
    :data-items="patients"
    :columns="columns"
    @rowclick="handleRowClick"
    @keydown="handleKeyDown"
  />
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { Grid } from "@progress/kendo-vue-grid";
import { useGridA11y, useGridRowAction } from "@featherk/composables/grid";

const gridRef = ref(null);
const patients = ref([{ id: 1, name: "Alice Johnson" }]);

const { handleGridKeyDown, initA11y } = useGridA11y(gridRef);
const { handleRowClick, handleRowKeyDown } = useGridRowAction({
  dataItems: patients,
  onRowAction: (patient) => {
    // Perform row action
  },
});

// Delegate keydown to both composable handlers
const handleKeyDown = (event: KeyboardEvent) => {
  handleGridKeyDown(event);
  handleRowKeyDown(event);
};

onMounted(() => {
  initA11y();
});
</script>
```

## Options Reference

`useGridRowAction(options: UseGridRowActionOptions<T>)` accepts the following configuration:

| Option                 | Type                                                  | Default      | Description                                                                                |
| :--------------------- | :---------------------------------------------------- | :----------- | :----------------------------------------------------------------------------------------- |
| `onRowAction`          | `(dataItem: T, context: RowActionContext<T>) => void` | **Required** | Callback executed when a valid row click or keyboard activation occurs.                    |
| `dataItems`            | `Ref<T[]> \| T[]`                                     | `undefined`  | Grid data array or ref used to resolve the `dataItem` on grid-level `@keydown` activation. |
| `gridRef`              | `Ref<any>`                                            | `undefined`  | Optional ref to Kendo Grid instance as an alternative `dataItem` lookup source.            |
| `ignoreSelectors`      | `string[]`                                            | `[]`         | Additional CSS selectors inside row cells that should prevent triggering row actions.      |
| `shouldIgnoreTarget`   | `(target: HTMLElement, event: Event) => boolean`      | `undefined`  | Custom predicate function for advanced target element filtering.                           |
| `enableKeyboardAction` | `boolean`                                             | `true`       | Whether to enable `Enter`/`Space` key activation on focused rows.                          |

## Returns Reference

`useGridRowAction` returns an object containing:

| Property           | Type                                                   | Description                                                                                                                  |
| :----------------- | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
| `handleRowClick`   | `(event: GridRowClickEvent) => void`                   | Primary click handler to bind to Kendo Grid's `@rowclick`.                                                                   |
| `handleRowKeyDown` | `(event: KeyboardEvent, explicitDataItem?: T) => void` | Primary keyboard handler to bind to Kendo Grid's `@keydown`. Accepts optional `explicitDataItem` if bound at slot/row level. |
| `isIgnoredTarget`  | `(target: HTMLElement, event?: Event) => boolean`      | Utility function to test if a given DOM element matches default or custom ignore selectors.                                  |

## Callback Context Reference

The `onRowAction` callback receives `(dataItem, context)` where `context` contains:

| Property      | Type                                | Description                                                                                                                                  |
| :------------ | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| `dataItem`    | `T`                                 | The row's data item.                                                                                                                         |
| `rowIndex`    | `number \| undefined`               | Row index in the grid data.                                                                                                                  |
| `field`       | `string \| undefined`               | Column field name if clicked on a specific cell.                                                                                             |
| `event`       | `Event`                             | Original native browser DOM event.                                                                                                           |
| `triggerType` | `'click' \| 'keyboard'`             | Interaction source that triggered the action (`click` or `keyboard`).                                                                        |
| `ctrlKey`     | `boolean`                           | `true` if Ctrl key was held down during activation.                                                                                          |
| `shiftKey`    | `boolean`                           | `true` if Shift key was held down during activation.                                                                                         |
| `metaKey`     | `boolean`                           | `true` if Cmd / Meta key was held down during activation.                                                                                    |
| `altKey`      | `boolean`                           | `true` if Alt key was held down during activation.                                                                                           |
| `coordinates` | `RowActionCoordinates \| undefined` | Activation coordinates when available. Mouse clicks use native pointer coordinates; keyboard activation uses the focused row's bounding box. |

### Row Action Coordinates

`RowActionCoordinates` is exported from `@featherk/composables` and contains the following values:

| Property             | Description                                                                                                                                              |
| :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clientX`, `clientY` | Coordinates relative to the browser viewport.                                                                                                            |
| `pageX`, `pageY`     | Coordinates relative to the full document, including scroll offset.                                                                                      |
| `screenX`, `screenY` | Coordinates relative to the physical screen for pointer activation. For keyboard activation, these are approximated from the row's viewport coordinates. |

The coordinates can be used to position a context menu, popover, tooltip, or other contextual UI near the activated row:

```ts
onRowAction: (dataItem, context) => {
  if (context.coordinates) {
    openContextMenu({
      dataItem,
      left: context.coordinates.clientX,
      top: context.coordinates.clientY,
    });
  }
},
```

For mouse activation, the values come from the native mouse or pointer event. For keyboard activation, there is no actual pointer location, so the composable derives the values from the focused row's bounding box using its left edge and bottom edge. The `coordinates` property is therefore optional.

## Default Ignored Selectors

The following elements and interactive controls are automatically ignored by default:

- `button`, `a`, `input`, `select`, `textarea`
- `[role="button"]`, `[role="checkbox"]`, `[role="link"]`, `[role="menuitem"]`, `[role="option"]`
- `.k-checkbox`, `.k-button`, `.k-dropdown`, `.k-picker`, `.k-hierarchy-cell`, `.k-grid-header-menu`, `.k-column-menu`
