import { Meta, Canvas } from '@storybook/addon-docs/blocks'
import * as OnRangeCopyStories from './on-range-copy.stories.tsx'

<Meta title="pv-grid/Components/SpreadsheetGrid (Experimental)/onRangeCopy" />

# onRangeCopy

The `onRangeCopy` prop accepts two presets (`labels` and `values`) or a callback function that is invoked when a user copies a selected range of cells within the `SpreadsheetGrid`. This function allows developers to choose what data ends up on the clipboard, enabling customization of the copied content.

## Type definition

`onRangeCopy` accepts `"labels"`, `"values"` or a callback function of type `SpreadsheetRangeCopy<TDataModel>`:

```tsx
export type SpreadsheetRangeCopy<TDataModel extends GridRowData> = (
    rangeSelection: GridRangeSelection<TDataModel>,
    data: {
        rowIds: TDataModel['id'][]
        columnIds: string[]
    },
    e: React.ClipboardEvent
) => void
```

## Presets

Note: both string presets will copy tab-delimited `text/plain` data and HTML formatted `text/html` data to the clipboard. You can test this by pasting into a text editor (like Notepad or VS Code) and a rich text editor (like Word or Google Docs) or a spreadsheet (like Excel or Google Sheets). Notepad will use the `text/plain` data, while Word and Excel will use the `text/html` data. In addition to these formats, the presets also write a custom mime type `application/x-pvds-grid` and use JSON to store both the value and label for each cell along with other data. When copying from one grid and pasting in the same or another grid, the `value` from the cell will be used regardless of if the preset is `labels` or `values`.

### `labels` (default)

When set to `"labels"`, the copied content will consist of each cell's label. See the [Cells documentation](/docs/pv-grid-components-grid-cells--docs) for more information on how cell labels are determined. It will be the same as `values` if the columns do not have a `label` function defined.

### `values`

When set to `"values"`, the copied content will consist of each cell's value. See the [Cells documentation](/docs/pv-grid-components-grid-cells--docs) for more information on how cell values are determined. This is useful if you format the label with currency and punctuation, but want to copy the raw number value.

Try changing which preset is in use and then copying a range of cells in the example below. You can paste the results into a text editor or spreadsheet to see the difference.

<Canvas of={OnRangeCopyStories.OnRangeCopy} />

## Custom callback

When a function is provided, it will be called with the selected range of cells, the row and column IDs in that range, and the clipboard event. You can use this to customize what data is copied to the clipboard.

We recommend you write both a plain text version and an HTML version of the copied data, but you can further customize what is copied by writing other MIME types to the clipboard if needed. See the [Clipboard API documentation](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) for more information.

Note: this is a synchronous callback. If you need to perform asynchronous operations (like fetching data) before writing to the clipboard, you will need to prompt the user for permission to access their clipboard and have it granted before continuing. Due to this restriction, we recommend you keep this operation synchronous.

### Example

In this custom callback example, the data will be copied with the column headers (which is not how our default presets copy data).

This is a complex example, but high level it performs the following steps:

- Gets the label for each columnId and rowId pair using the SpreadsheetGrid API
- Builds a plain text version of the copied data with tab and newline delimiters
- Builds an HTML table version of the copied data with `<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>` and `<td>` elements
- Writes both versions to the clipboard

<Canvas of={OnRangeCopyStories.CustomOnRangeCopy} />

### Custom callback Code

```tsx
const onRangeCopy = useCallback(
    (
        _rangeSelection: GridRangeSelection<User>,
        data: {
            rowIds: User['id'][]
            columnIds: string[]
        },
        e: React.ClipboardEvent
    ) => {
        const gridApi = apiRef.current
        if (!gridApi) {
            return
        }
        const doc = document.implementation.createHTMLDocument()

        const rows = doc.createElement('tbody')
        const rowsPlain: string[] = []

        const headerRows = doc.createElement('thead')
        const headerCellsPlain: string[] = []
        const headerRow = doc.createElement('tr')

        for (const columnId of data.columnIds) {
            const headerCell = doc.createElement('th')
            const headerContent = gridApi.content.getColumnLabel(columnId)
            headerCell.textContent = headerContent ?? ''
            headerRow.appendChild(headerCell)
            headerCellsPlain.push(headerContent)
        }

        rowsPlain.push(headerCellsPlain.join('\t'))
        headerRows.appendChild(headerRow)

        for (const rowId of data.rowIds) {
            const cells = doc.createElement('tr')
            const cellsPlain: string[] = []
            for (const columnId of data.columnIds) {
                const cellContent = gridApi.content.getLabel({
                    rowId,
                    columnId,
                })
                const cell = doc.createElement('td')
                cell.textContent = cellContent ?? ''
                cells.appendChild(cell)
                cellsPlain.push(cellContent)
            }

            rows.appendChild(cells)
            rowsPlain.push(cellsPlain.join('\t'))
        }

        const table = doc.createElement('table')
        table.appendChild(headerRows)
        table.appendChild(rows)
        doc.body.appendChild(table)

        // Write both plain text and HTML versions to the clipboard
        e.clipboardData?.setData('text/plain', rowsPlain.join('\n'))
        e.clipboardData?.setData('text/html', doc.documentElement.outerHTML)
    },
    []
)
```
