-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Summary
A new API that allows extending the base cell renderer with composable "decorators" — custom rendering areas around or over the standard cell content, where the base text renderer automatically adjusts its available area.
Problem
Currently there are two extremes:
- Standard cell rendering — just text with theme styles, no extensibility
- Custom
CellTypeRenderer.render()— completely replaces the text rendering pipeline, consumer must re-implement text layout, truncation, wrapping
There is nothing in between. For common patterns like "standard text cell + expander icon on the left" or "standard text cell + sort icon on the right", consumers must either:
- Write a full custom renderer (duplicating text layout logic)
- Create an overlay
RenderLayerthat paints over the engine output
Proposed API
interface CellDecorator {
id: string;
position: "left" | "right" | "overlay" | "underlay";
// How much space to reserve (for left/right positions)
getWidth(cellData: CellData, cellHeight: number, theme: SpreadsheetTheme): number;
// Render the decorator in its allocated area
render(ctx: CanvasRenderingContext2D,
x: number, y: number, width: number, height: number,
cellData: CellData, theme: SpreadsheetTheme): void;
// Optional: declare hit zones within the decorator area
getHitZones?(width: number, height: number, cellData: CellData): CellHitZone[];
}Usage example — expander decorator:
const expanderDecorator: CellDecorator = {
id: "tree-expander",
position: "left",
getWidth(cellData, cellHeight) {
const level = cellData.metadata?.hierarchyLevel ?? 0;
const hasChildren = cellData.metadata?.hasChildren;
return level * 20 + (hasChildren ? 16 : 0); // indent + icon
},
render(ctx, x, y, w, h, cellData, theme) {
// Draw indent + expander triangle
},
getHitZones(w, h, cellData) {
return [{ id: "toggle", x: w - 16, y: (h - 12) / 2, width: 12, height: 12 }];
}
};
engine.getCellTypeRegistry().addDecorator("tree-expander", expanderDecorator, {
appliesTo: (row, col, cellData) => cellData.metadata?.hierarchyLevel != null
});How it works in the rendering pipeline:
CellTextLayercollects applicable decorators for each cell- Left decorators: reserve width from the left → text area starts after them
- Right decorators: reserve width from the right → text truncation width reduced
- Underlay decorators: render before text (e.g., progress bar background)
- Overlay decorators: render after text (e.g., status badges)
- Text layout uses the remaining area after decorator space is reserved
Benefits:
- Base text rendering (font, color, alignment, truncation, wrapping) stays intact
- Decorators compose — multiple left/right decorators stack
- Hit zones integrate with the sub-cell hit testing system (issue Sub-cell hit testing API: clickable zones within cells #4)
- Covers expanders, sort icons, action buttons, progress bars, status indicators
Use Cases
- Tree/hierarchy expanders (left decorator with indent)
- Sort indicators in data cells (right decorator)
- Action buttons (right decorator with hit zone)
- Progress bar backgrounds (underlay decorator)
- Validation/status badges (overlay decorator)
- Images before/after text (left/right decorator)
Related
- Per-cell CellStyle rendering: bgColor, textColor, font, alignment, borders #3 — Per-cell CellStyle rendering
- Sub-cell hit testing API: clickable zones within cells #4 — Sub-cell hit testing API
Files to modify
packages/core/src/types/cell-type-registry.ts—CellDecoratorinterface, registrationpackages/core/src/renderer/layers/cell-text-layer.ts— decorator space calculation, renderingpackages/core/src/types/interfaces.ts— new types
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request