ColumnConverter#

@palmyralabs/rt-forms · src/palmyra/grid/base/utils/ColumnConverter.tsx

Overview#

Not a component — a factory function that compiles a list of Palmyra-flavoured ColumnDefinitions into @tanstack/react-table ColumnDefs. Handles grouping (columnGroup), sort/search flags, value conversion (via FormatterFactory), and per-column cellRenderer overrides. Dispatches header/footer/cell rendering through a GridCustomizer so themes (Mantine / MUI / plain) can inject their own chrome.

Grid templates like SummaryGrid call this under the hood; you’ll mostly touch it when building a bespoke grid.

Exports#

function generateColumns(
  columnDefs: ColumnDefinition[],
  customizer?: GridCustomizer,
): ColumnDef<RowData, any>[];

ColumnDefinition shape#

interface ColumnDefinition {
  attribute:    string;                       // bean attribute (matches the backend @PalmyraField)
  name:         string;                       // stable id
  label?:       string;                       // header text
  type?:        string;                       // 'STRING' | 'NUMERIC' | 'DATE' | 'BIT' | ...
  sortable?:    boolean;
  searchable?:  boolean;
  width?:       number | string;
  columnGroup?: string;                       // groups adjacent columns under one header
  cellRenderer?: (ctx: CellContext<any, any>) => React.ReactNode;
  displayPattern?: any;                       // consumed by CellFormatter for BIT / enum columns
  // ... more fields from src/palmyra/grid/base/types.ts
}

Example — bespoke grid built from definitions#

import { useMemo } from 'react';
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { generateColumns, type ColumnDefinition } from '@palmyralabs/rt-forms';

const defs: ColumnDefinition[] = [
  { attribute: 'name',          name: 'name',          label: 'Name',    searchable: true, sortable: true },
  { attribute: 'contactMobile', name: 'contactMobile', label: 'Mobile' },
  { attribute: 'rating',        name: 'rating',        label: 'Rating', type: 'NUMERIC', sortable: true },
  { attribute: 'active',        name: 'active',        label: 'Active', type: 'BIT',
    displayPattern: [{ '1': 'Yes' }, { '0': 'No' }] },
];

export function ManufacturerTable({ rows }: { rows: any[] }) {
  const columns = useMemo(() => generateColumns(defs), []);
  const table   = useReactTable({ data: rows, columns, getCoreRowModel: getCoreRowModel() });

  return (
    <table>
      <thead>{table.getHeaderGroups().map(hg => (
        <tr key={hg.id}>{hg.headers.map(h => (
          <th key={h.id}>{flexRender(h.column.columnDef.header, h.getContext())}</th>
        ))}</tr>
      ))}</thead>
      <tbody>{table.getRowModel().rows.map(r => (
        <tr key={r.id}>{r.getVisibleCells().map(c => (
          <td key={c.id}>{flexRender(c.column.columnDef.cell, c.getContext())}</td>
        ))}</tr>
      ))}</tbody>
    </table>
  );
}

The second argument — customizer — lets a theme library replace headers, footers, and cell wrappers without rewriting the whole grid.