Cell Types & Registry
Cell Types & Registry
Section titled “Cell Types & Registry”Every column (and individual cell) has a cell type that controls how values are formatted, aligned, and rendered on the canvas. The CellTypeRegistry manages all type renderers and supports custom types.
View source code
import { useRef, useEffect } from 'react';import { WitTable } from '@witqq/spreadsheet-react';import type { WitTableRef } from '@witqq/spreadsheet-react';import type { ColumnDef } from '@witqq/spreadsheet';import { DemoWrapper } from './DemoWrapper';import { useSiteTheme } from './useSiteTheme';
const columns: ColumnDef[] = [ { key: 'id', title: 'ID', width: 50, type: 'number' }, { key: 'text', title: 'String', width: 120, type: 'string' }, { key: 'amount', title: 'Number', width: 100, type: 'number' }, { key: 'active', title: 'Boolean', width: 80, type: 'boolean' }, { key: 'created', title: 'Date', width: 110, type: 'date' }, { key: 'progress', title: 'Progress', width: 140, type: 'progressBar' as any }, { key: 'rating', title: 'Rating', width: 120, type: 'rating' as any },];
const data = [ { id: 1, text: 'Hello World', amount: 1234.56, active: true, created: '2025-01-15', progress: 85, rating: 5 }, { id: 2, text: 'Test Data', amount: -42, active: false, created: '2024-06-01', progress: 45, rating: 3 }, { id: 3, text: 'Long text that will be truncated in the cell', amount: 0, active: true, created: '2023-12-25', progress: 10, rating: 1 }, { id: 4, text: '', amount: 99999, active: false, created: '2025-03-01', progress: 100, rating: 4 }, { id: 5, text: 'Final Row', amount: 500, active: true, created: '2025-02-14', progress: 62, rating: 2 },];
export function CellTypesDemo() { const { witTheme } = useSiteTheme(); const tableRef = useRef<WitTableRef>(null);
useEffect(() => { const engine = tableRef.current?.getInstance(); if (!engine) return; const registry = engine.getCellTypeRegistry();
registry.register('progressBar' as any, { format: (value) => `${value}%`, align: 'left', render: (ctx, value, x, y, width, height, theme) => { const pct = Math.max(0, Math.min(100, Number(value) || 0)); const barWidth = (width - 8) * (pct / 100); const barHeight = 12; const barY = y + (height - barHeight) / 2; ctx.fillStyle = theme.colors.cellBorder; ctx.fillRect(x + 4, barY, width - 8, barHeight); ctx.fillStyle = pct >= 80 ? '#63be7b' : pct >= 50 ? '#ffeb84' : '#f8696b'; ctx.fillRect(x + 4, barY, barWidth, barHeight); ctx.fillStyle = theme.colors.cellText; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(`${pct}%`, x + width / 2, y + height / 2); }, });
registry.register('rating' as any, { format: (value) => '★'.repeat(Number(value) || 0), align: 'center', render: (ctx, value, x, y, width, height) => { const rating = Math.max(0, Math.min(5, Math.round(Number(value) || 0))); const starSize = 14; const totalWidth = starSize * 5; const startX = x + (width - totalWidth) / 2; const centerY = y + height / 2 + 5; ctx.font = `${starSize}px sans-serif`; for (let i = 0; i < 5; i++) { ctx.fillStyle = i < rating ? '#f4b400' : '#e0e0e0'; ctx.fillText('★', startX + i * starSize, centerY); } }, });
engine.requestRender(); }, []);
return ( <DemoWrapper title="Live Demo" description="All cell types in one table: string, number (right-aligned), boolean (checkbox), date, progress bar, and star rating." height={300}> <WitTable theme={witTheme} ref={tableRef} columns={columns} data={data} showRowNumbers style={{ width: '100%', height: '100%' }} /> </DemoWrapper> );}CellType
Section titled “CellType”type CellType = | 'string' | 'number' | 'boolean' | 'date' | 'select' | 'dynamicSelect' | 'formula' | 'link' | 'image' | 'progressBar' | 'rating' | 'badge' | 'custom';Set the type on a column definition:
const columns: ColumnDef[] = [ { key: 'name', title: 'Name', width: 200, type: 'string' }, { key: 'price', title: 'Price', width: 100, type: 'number' }, { key: 'active', title: 'Active', width: 80, type: 'boolean' }, { key: 'created', title: 'Created', width: 120, type: 'date' },];CellTypeRenderer Interface
Section titled “CellTypeRenderer Interface”Each cell type maps to a renderer:
interface CellTypeRenderer { format(value: CellValue): string; align: CellAlignment; // 'left' | 'center' | 'right' render?: ( ctx: CanvasRenderingContext2D, value: CellValue, x: number, y: number, width: number, height: number, theme: WitTheme ) => void;}| Method | Description |
|---|---|
format(value) | Convert a CellValue to display string |
align | Text alignment within the cell |
render | Optional custom canvas drawing (bypasses default text rendering) |
Built-in Renderers
Section titled “Built-in Renderers”| Type | Alignment | Formatting |
|---|---|---|
string | left | Returns value as-is (String(value)) |
number | right | Locale-formatted number (toLocaleString()) |
boolean | center | Renders a checkbox glyph via custom render |
date | left | toLocaleDateString() for Date values |
CellTypeRegistry
Section titled “CellTypeRegistry”class CellTypeRegistry { get(type: CellType): CellTypeRenderer; register(type: CellType, renderer: CellTypeRenderer): void; detectType(value: CellValue): CellType;}get(type)— returns the renderer for a type (falls back tostringif not found)register(type, renderer)— register or override a type rendererdetectType(value)— auto-detect type from a value (number, boolean, Date, or string)
Custom Progress Bar Type
Section titled “Custom Progress Bar Type”engine.getCellTypeRegistry().register('progressBar', { format: (value) => `${value}%`, align: 'left', render: (ctx, value, x, y, width, height, theme) => { const pct = Math.max(0, Math.min(100, Number(value) || 0)); const barWidth = (width - 8) * (pct / 100); const barHeight = 12; const barY = y + (height - barHeight) / 2;
// Background track ctx.fillStyle = theme.colors.cellBorder; ctx.fillRect(x + 4, barY, width - 8, barHeight);
// Filled portion ctx.fillStyle = pct >= 80 ? '#63be7b' : pct >= 50 ? '#ffeb84' : '#f8696b'; ctx.fillRect(x + 4, barY, barWidth, barHeight);
// Label ctx.fillStyle = theme.colors.cellText; ctx.textAlign = 'center'; ctx.fillText(`${pct}%`, x + width / 2, y + height / 2 + 4); },});Custom Rating Stars Type
Section titled “Custom Rating Stars Type”engine.getCellTypeRegistry().register('rating', { format: (value) => '★'.repeat(Number(value) || 0), align: 'center', render: (ctx, value, x, y, width, height, theme) => { const rating = Math.max(0, Math.min(5, Math.round(Number(value) || 0))); const starSize = 14; const totalWidth = starSize * 5; const startX = x + (width - totalWidth) / 2; const centerY = y + height / 2 + 5;
ctx.font = `${starSize}px sans-serif`; for (let i = 0; i < 5; i++) { ctx.fillStyle = i < rating ? '#f4b400' : '#e0e0e0'; ctx.fillText('★', startX + i * starSize, centerY); } },});Use the custom type in a column:
const columns: ColumnDef[] = [ { key: 'progress', title: 'Progress', width: 150, type: 'progressBar' }, { key: 'rating', title: 'Rating', width: 120, type: 'rating' },];