import { IRenderer, IRenderContext } from './IGroupingRenderer'; /** * Entity must have id */ export interface IGroupingEntity { id: string; } /** * Configuration for a grouping renderer */ export interface IGroupingRendererConfig { elementTag: string; // e.g., 'swp-team-header' idAttribute: string; // e.g., 'teamId' -> data-team-id colspanVar: string; // e.g., '--team-cols' } /** * Abstract base class for grouping renderers * * Handles: * - Fetching entities by IDs * - Calculating colspan from parentChildMap * - Creating header elements * - Appending to container * * Subclasses override: * - renderHeader() for custom content * - getDisplayName() for entity display text */ export abstract class BaseGroupingRenderer implements IRenderer { abstract readonly type: string; protected abstract readonly config: IGroupingRendererConfig; /** * Fetch entities from service */ protected abstract getEntities(ids: string[]): Promise; /** * Get display name for entity */ protected abstract getDisplayName(entity: T): string; /** * Main render method - handles common logic */ async render(context: IRenderContext): Promise { const allowedIds = context.filter[this.type] || []; if (allowedIds.length === 0) return; const entities = await this.getEntities(allowedIds); const dateCount = context.filter['date']?.length || 1; const childIds = context.childType ? context.filter[context.childType] || [] : []; for (const entity of entities) { const entityChildIds = context.parentChildMap?.[entity.id] || []; const childCount = entityChildIds.filter(id => childIds.includes(id)).length; const colspan = childCount * dateCount; const header = document.createElement(this.config.elementTag); header.dataset[this.config.idAttribute] = entity.id; header.style.setProperty(this.config.colspanVar, String(colspan)); // Allow subclass to customize header content this.renderHeader(entity, header, context); context.headerContainer.appendChild(header); } } /** * Override this method for custom header rendering * Default: just sets textContent to display name */ protected renderHeader(entity: T, header: HTMLElement, _context: IRenderContext): void { header.textContent = this.getDisplayName(entity); } /** * Helper to render a single entity header. * Can be used by subclasses that override render() but want consistent header creation. */ protected createHeader(entity: T, context: IRenderContext): HTMLElement { const header = document.createElement(this.config.elementTag); header.dataset[this.config.idAttribute] = entity.id; this.renderHeader(entity, header, context); return header; } }