apply<T extends DataGridRow> method

  1. @override
Future<DataGridState<T>?> apply<T extends DataGridRow>(
  1. EventContext<T> context
)
override

Apply this event's transformation to the state. Returns the new state, or null if no state change should occur. Can return a Future for async operations.

Implementation

@override
Future<DataGridState<T>?> apply<T extends DataGridRow>(
  EventContext<T> context,
) async {
  final state = context.state;
  final focused = state.selection.focusedCells;
  if (focused.isEmpty) return null;

  // Build a column-id → column map for fast lookup
  final columnMap = <int, DataGridColumn<T>>{
    for (final col in state.columns) col.id: col,
  };

  // Parse and sort cells: by row display-order index, then column index
  final visibleColumns =
      state.effectiveColumns.where((c) => c.visible).toList();
  final colIndexMap = <int, int>{
    for (int i = 0; i < visibleColumns.length; i++) visibleColumns[i].id: i,
  };
  final rowIndexMap = <double, int>{
    for (int i = 0; i < state.displayOrder.length; i++)
      state.displayOrder[i]: i,
  };

  final parsed = <({int rowIdx, int colIdx, double rowId, int colId})>[];
  for (final cellId in focused) {
    final (rowId, colId) = parseCellId(cellId);
    final rowIdx = rowIndexMap[rowId];
    final colIdx = colIndexMap[colId];
    if (rowIdx == null || colIdx == null) continue;
    parsed.add((rowIdx: rowIdx, colIdx: colIdx, rowId: rowId, colId: colId));
  }

  // Build (rowIdx, colIdx) → value map
  final cellValues = <(int, int), String>{};
  for (final cell in parsed) {
    final row = state.rowsById[cell.rowId];
    final col = columnMap[cell.colId];
    final value = (row != null && col?.valueAccessor != null)
        ? col!.valueAccessor!(row)?.toString() ?? ''
        : '';
    cellValues[(cell.rowIdx, cell.colIdx)] = value;
  }

  // Collect unique sorted row and column indices
  final rowIndices = parsed.map((c) => c.rowIdx).toSet().toList()..sort();
  final colIndices = parsed.map((c) => c.colIdx).toSet().toList()..sort();

  // Build CSV: for every selected row, output a value for every selected
  // column index in order, using '' for gaps (cells not in the selection).
  // Values are quoted when they contain a comma, double-quote, or newline.
  String csvQuote(String value) {
    if (value.contains(',') || value.contains('"') || value.contains('\n')) {
      return '"${value.replaceAll('"', '""')}"';
    }
    return value;
  }

  final lines = rowIndices.map((rowIdx) {
    return colIndices
        .map((colIdx) => csvQuote(cellValues[(rowIdx, colIdx)] ?? ''))
        .join(',');
  });

  final csv = lines.join('\n');
  await Clipboard.setData(ClipboardData(text: csv));

  return null;
}