1771 Technologies Logo

Cells

Canvas Cell Renderer

The GraphiteGridCanvas renderer draws to an HTML canvas element. You can define a custom cell renderer for the canvas using the cellCanvasRenderer property on the column definition.

The cellCanvasRenderer property is an object that provides two properties:

  • drawFunc: a function called to draw to a cell.
  • drawPortal: a function that returns a React component relative to the cell's top-left corner.

Both properties can be provided, allowing you to mix DOM functionality with the Canvas Cell.

Using the drawFunc Property

The example below demonstrates a custom canvas cell renderer that draws a basic sparkline based on the row data provided to the grid.

Info

The drawFunc receives a CellCanvasRendererParams object, which contains viewX and viewY properties. These properties represent the x and y coordinates of the grid cell relative to the viewport. Any drawing must be bounded by viewX, viewY, and the cell's width and height. Graphite Grid clips content outside the rectangle described by the points:

  • Top-left: viewX, viewY
  • Bottom-right: viewX + width, viewY + height
const rowData = [
  ["Rob Co", 1884, [4000, 3000, 2000, 2780, 1890, 2390, 3490]],
  ["Cats R Us", 1993, [4000, 2100, 3000, 2280, 1890, 4390, 1490]],
  ["Carl Inc", 2024, [1000, 3020, 1500, 2780, 1800, 2900, 1890]],
];
 
function CanvasCellRenderer<T>({
  ctx,
  viewX,
  viewY,
  width,
  height,
  theme,
  field,
}: CellCanvasRendererParams<T>) {
  if (!Array.isArray(field)) return;
 
  const widthSpacing = width / field.length;
  const maxHeight = Math.max(...field);
 
  ctx.beginPath();
  ctx.moveTo(viewX, viewY + height - 2);
  ctx.strokeStyle = theme.color;
  ctx.lineWidth = 1.5;
  ctx.lineCap = "round";
 
  for (let i = 1; i <= field.length; i++) {
    const dataPoint = field[i - 1];
    const x = viewX + widthSpacing * i;
    const y = viewY + height * (dataPoint / maxHeight);
 
    ctx.lineTo(x, y);
  }
  ctx.stroke();
}
 
export function CanvasCellRendererExample() {
  const grid = useGraphiteGrid({
    initial: {
      columnDefinitions: [
        { id: "company-name", headerLabel: "Company Name", field: 0 },
        { id: "founded", headerLabel: "Founded", field: 1 },
        {
          id: "performance",
          headerLabel: "Performance",
          field: 2,
          cellCanvasRenderer: { drawFunc: CanvasCellRenderer },
        },
      ],
      // other props
    },
  });
 
  return (
    <div style={{ height: 200 }}>
      <GraphiteGridCanvas state={grid} />
    </div>
  );
}

Using the drawPortal Property

Adding interactivity to cells in the Canvas Grid can be complex. Cell events should suffice for cell-level interactions. However, for interactions concerning specific elements within a cell, the drawPortal property is a better option.

The drawPortal property is a function that returns a React component. This component is positioned relative to the grid cell on the canvas, allowing you to mix canvas drawings with DOM elements.

In the example below, a portal renderer displays two buttons that increment and decrease the item count.

const countData: [string, number][] = [
  ["apple", 0],
  ["banana", 0],
  ["orange", 0],
];
 
function IncrDecrPortal({ api, row }: CellRendererParams<[string, number]>) {
  return (
    <div>
      <button
        onClick={() => {
          const source = api.getRowDataSource();
          if (source?.kind !== "client") return;
 
          const data = source.data;
          data[row.rowIndex!][1]++;
 
          api.refreshRowDataSource();
        }}
      >
        Incr
      </button>
      <button
        onClick={() => {
          // similar to increment
        }}
      >
        Decr
      </button>
    </div>
  );
}
 
export function CanvasCellDrawPortal() {
  const grid = useGraphiteGrid<[string, number]>({
    initial: {
      columnDefinitions: [
        { id: "item", headerLabel: "Item", field: 0 },
        { id: "count", headerLabel: "Count", field: 1 },
        {
          id: "buttons",
          headerLabel: "Incr/Decr",
          field: 2,
          cellCanvasRenderer: {
            drawPortal: IncrDecrPortal,
          },
        },
      ],
      // other props
    },
  });
 
  return (
    <div style={{ height: 200 }}>
      <GraphiteGridCanvas state={grid} />
    </div>
  );
}

By leveraging the cellCanvasRenderer property and its drawFunc and drawPortal functions, you can create custom cell renderers for the Canvas Grid that suit your application's specific needs.