import { useCallback } from 'react' import { useGridContext, useGridSelector } from '../context/grid-context' import type { GridRangeSelection, GridRowData } from '../types' import type { SpreadsheetRangeCopy } from '../grids/spreadsheet-grid/types' const RANGE_COPY_ANIMATION_DURATION = 300 export const useRangeCopy = ( onRangeCopyIncoming: 'values' | 'labels' | SpreadsheetRangeCopy, container: HTMLElement | null ) => { const grid = useGridContext() const isSpreadsheet = useGridSelector(grid.selectors.selectIsSpreadsheet) const onRangeCopy = useCallback( ( _rangeSelection: GridRangeSelection, data: { rowIds: GridRowData['id'][] columnIds: string[] }, e: React.ClipboardEvent ) => { const doc = document.implementation.createHTMLDocument() const rows = doc.createElement('tbody') const rowsPlain: string[] = [] const customData: { rowId: GridRowData['id'] values: Record< string, { label: string; value: unknown; colSpan?: number } > }[] = [] for (const rowId of data.rowIds) { const cells = doc.createElement('tr') const cellsPlain: string[] = [] const customValues: Record< string, { label: string; value: unknown; colSpan?: number } > = {} const columnSpanConfigs = grid.selectors.selectColumnSpanByRowId( grid.getState(), rowId ) for (const columnId of data.columnIds) { const colSpanConfig = columnSpanConfigs?.get(columnId) if (colSpanConfig?.skip || colSpanConfig?.as) { cellsPlain.push('') continue } const relatedColumns = colSpanConfig?.positionColumnIds?.filter((id) => data.columnIds.includes(id) ) ?? [columnId] const cellValue = grid.api.content.getValue({ rowId, columnId, }) const cellLabel = grid.api.content.getLabel({ rowId, columnId, }) customValues[columnId] = { label: cellLabel ?? '', value: cellValue, colSpan: relatedColumns.length, } const cellContent = onRangeCopyIncoming === 'values' ? cellValue : cellLabel const cell = doc.createElement('td') if (relatedColumns.length > 1) { cell.colSpan = relatedColumns.length } cell.textContent = cellContent ?? '' cells.appendChild(cell) cellsPlain.push(cellContent) } customData.push({ rowId, values: customValues }) rows.appendChild(cells) rowsPlain.push(cellsPlain.join('\t')) } const table = doc.createElement('table') table.appendChild(rows) doc.body.appendChild(table) e.clipboardData?.setData('text/plain', rowsPlain.join('\n')) e.clipboardData?.setData('text/html', doc.documentElement.outerHTML) e.clipboardData?.setData( 'application/x-pvds-grid', JSON.stringify(customData) ) }, [grid, onRangeCopyIncoming] ) const handleCopy = useCallback( (e: React.ClipboardEvent) => { const state = grid.getState() const range = grid.selectors.selectRangeSelection(state) if (!range) { return } const rangeData = grid.selectors.selectRangeIds(state) e.preventDefault() if (typeof onRangeCopyIncoming === 'string') { onRangeCopy(range, rangeData, e) } else { onRangeCopyIncoming(range, rangeData, e) } container?.setAttribute('data-pvds-grid-range-copied', 'true') setTimeout(() => { container?.removeAttribute('data-pvds-grid-range-copied') }, RANGE_COPY_ANIMATION_DURATION) }, [grid, container, onRangeCopy, onRangeCopyIncoming] ) if (!isSpreadsheet) { return undefined } return handleCopy }