Esposter

Copy computed values

Range copy includes computed-column values, matching the Excel expectation that copying a formula cell yields its value.

How it works

useCopyRangeToClipboard used to slice the selected columns and rows and hand the sub-DataSource straight to copyToClipboard, which serializes each cell from stored row.data. Computed columns store nothing there, so their cells serialized empty.

Copy now materializes through the same path export uses: filterDataSourceColumns resolves every selected cell through computeValue — the one resolver headers, cell render, and export all share — against the full row and column context. The selection's row range bounds the clone/compute work to the selected rows (aggregation transformations still see every row), so copying a small range in a large sheet stays cheap. Non-computed columns are unaffected (computeValue returns row.data[column.name] for them), so the only behavioural change is computed cells carrying their displayed value. There is one materialization code path, not a copy-specific fork.

A cell range indexes the displayed columns, while computeValue resolves a computed column's source by id against the columns it is handed and answers null for a source it cannot find — so the two sets are not interchangeable. filterDataSourceRange derives both from the one DataSource: the displayed columns select which cells the range covers, and the full column set stays the compute context. Hiding the source column of a computed column therefore changes nothing about what copy produces, matching the grid, which has always passed every column.

Paste needs no change: computed columns are read-only and every write site already skips them, so pasting over a computed cell stays a no-op. The copied value simply lands wherever the user pastes it — another column, Excel, Sheets — exactly the Excel semantics.

flowchart LR
  R[Selected range] --> D[filterDataSourceRange]
  D -- "displayed columns select the cells" --> F[filterDataSourceColumns]
  D -- "full column set is the compute context" --> F
  F --> C[computeValue per cell]
  C --> S[Sliced sub-DataSource]
  S --> CB[copyToClipboard TSV and HTML]

Key files

Paths relative to packages/app/app.

FileRole
composables/resource/sheet/useCopyRangeToClipboard.tsMaterializes the selection via filterDataSourceRange
services/resource/sheet/dataSource/filterDataSourceRange.tsDerives the range's columns and the compute context
services/resource/sheet/dataSource/filterDataSourceColumns.tsThe shared resolver that materializes computed values
services/resource/sheet/column/computeValue.tsPer-cell resolver used by render, export, and now copy

Notes

  • Copy is a hot interactive path, but a selection is small and computeValue is already evaluated per cell on render, so materializing the same cells once more on Ctrl+C is negligible.
  • A chained or cyclic computed column resolves exactly as it renders (null → empty cell), so copy can never be worse than what the grid shows.