Structured Cell Renderer

Create your own custom schema on cell and render custom cells like Sparklines, document objects etc

The Structured Cell Renderer allows you to render custom visualizations and content within spreadsheet cells. Instead of displaying plain text, you can render rich content like sparklines, charts, or any custom component.

Overview

Structured results are special cell values that contain both data and rendering instructions. The spreadsheet automatically detects these structured values and renders them using the StructuredResultRenderer component.

Supported Structured Results

Sparklines

Sparklines are small inline charts that fit within a cell, perfect for visualizing trends in data.

import type { SparkLineResult } from "@rowsncolumns/common-types";

const sparklineValue: SparkLineResult = {
  kind: "sparkline",
  data: [1, 5, 3, 7, 4, 9, 6],
  formattedValue: "Trend", // Fallback text
};

The sparkline will automatically render as a mini chart within the cell.

Custom Structured Results

You can create your own structured result types by extending the StructuredResult interface:

import type { StructuredResult } from "@rowsncolumns/common-types";

type MyCustomResult = {
  kind: "custom";
  data: any;
  formattedValue: string;
} & StructuredResult;

Using Structured Cells

1. Define Your Custom Cell Data Type

import type { CellData, StructuredResult } from "@rowsncolumns/spreadsheet";

type MyStructuredResult = {
  kind: "sparkline" | "custom";
  data: any[];
  formattedValue: string;
} & StructuredResult;

type MyCustomCellData = CellData<MyStructuredResult>;

2. Create Structured Cell Values

import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";

const { onChangeBatch } = useSpreadsheetState({
  // ... other props
});

// Add a sparkline to a cell
onChangeBatch(
  sheetId,
  {
    startRowIndex: 1,
    endRowIndex: 1,
    startColumnIndex: 1,
    endColumnIndex: 1,
  },
  [{
    ev: {
      structuredValue: {
        kind: "sparkline",
        data: [1, 5, 3, 7, 4, 9, 6],
        formattedValue: "Trend",
      }
    }
  }]
);

3. Custom Renderer (Optional)

If you need custom rendering logic beyond sparklines, you can provide your own StructuredResult component to CanvasGrid:

import { Text } from "react-konva";
import type { StructuredResultProps } from "@rowsncolumns/spreadsheet";

const MyStructuredRenderer = (props: StructuredResultProps) => {
  const { structuredValue, ...rest } = props;
  
  if (structuredValue?.kind === "custom") {
    return (
      <Text
        {...rest}
        text={`Custom: ${structuredValue.formattedValue}`}
        fill="blue"
      />
    );
  }
  
  // Fallback to default renderer
  return null;
};

// Use in CanvasGrid
<CanvasGrid
  StructuredResult={MyStructuredRenderer}
  // ... other props
/>

Getting Structured Values

You can access structured cell values through the spreadsheet state:

const { getUserEnteredExtendedValue, getEffectiveExtendedValue } = useSpreadsheetState({
  // ... props
});

// Get the user-entered structured value
const userValue = getUserEnteredExtendedValue(sheetId, rowIndex, columnIndex);
console.log(userValue?.structuredValue);

// Get the computed/formatted structured value
const effectiveValue = getEffectiveExtendedValue(sheetId, rowIndex, columnIndex);
console.log(effectiveValue?.structuredValue);

Formulas and Structured Results

You can create formulas that return structured results:

import type FormulaParser from "@rowsncolumns/fast-formula-parser";
import type { FunctionArgument } from "@rowsncolumns/calculator";

const SPARKLINE = (parser: FormulaParser, dataRange: FunctionArgument) => {
  // Extract values from the range
  const values = dataRange.value; // Array of cell values
  
  return {
    kind: "sparkline",
    data: values.map(Number),
    formattedValue: `Sparkline(${values.length} points)`,
  };
};

// Register the function
const functions = {
  ...defaultFunctions,
  SPARKLINE,
};

// Use in a cell
// =SPARKLINE(A1:A10)

Complete Example

import React, { useState } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  type CellData,
  type StructuredResult,
} from "@rowsncolumns/spreadsheet";
import { useSpreadsheetState } from "@rowsncolumns/spreadsheet-state";

type MyStructuredResult = {
  kind: "sparkline";
  data: number[];
  formattedValue: string;
} & StructuredResult;

type MyCustomCellData = CellData<MyStructuredResult>;

function SpreadsheetWithStructuredCells() {
  const [sheets, setSheets] = useState([
    { sheetId: 1, rowCount: 100, columnCount: 26, title: "Sheet 1" }
  ]);
  const [sheetData, setSheetData] = useState<SheetData<MyCustomCellData>>({
    1: [
      null,
      {
        values: [
          null,
          {
            ev: {
              structuredValue: {
                kind: "sparkline",
                data: [1, 5, 3, 7, 4, 9, 6, 8, 5],
                formattedValue: "Sales Trend",
              },
            },
          },
        ],
      },
    ],
  });

  const {
    activeCell,
    activeSheetId,
    selections,
    getCellData,
    // ... other hook values
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets: setSheets,
    onChangeSheetData: setSheetData,
  });

  return (
    <SpreadsheetProvider>
      <CanvasGrid
        sheetId={activeSheetId}
        activeCell={activeCell}
        selections={selections}
        getCellData={getCellData}
        // ... other props
      />
    </SpreadsheetProvider>
  );
}

Use Cases

Sparklines are perfect for showing trends in financial data, metrics, or time series:

// Sales data with inline trend visualization
const salesData = {
  ev: {
    structuredValue: {
      kind: "sparkline",
      data: monthlySales, // [100, 120, 115, 140, ...]
      formattedValue: "$1,234 total",
    },
  },
};

Custom Indicators

Create custom visual indicators for status, progress, or ratings:

type StatusResult = {
  kind: "status";
  status: "good" | "warning" | "error";
  formattedValue: string;
} & StructuredResult;

Performance Considerations

  • Structured results are rendered using canvas (react-konva), providing excellent performance even with many cells

  • Complex visualizations should be optimized to avoid rendering bottlenecks

  • Consider using formattedValue as a text fallback for export and copy operations

Limitations

  • Structured results are visual-only and don't affect cell value calculations

  • When copying cells with structured results, the formattedValue is used as text

  • Excel export will show the formattedValue as plain text

Last updated

Was this helpful?