Cuatro nuevos componentes sgcHTML para datos estructurados: TreeGrid, PivotTable, ActivityFeed y Splitter | Blog de eSeGeCe

Cuatro nuevos componentes sgcHTML para datos estructurados: TreeGrid, PivotTable, ActivityFeed y Splitter

· Componentes

sgcHTML acaba de crecer con 25 componentes nuevos, y esta es la primera de cinco publicaciones que los recorren por temática. Empezamos con cuatro componentes construidos en torno a datos estructurados y cambiantes: TreeGrid para filas jerárquicas, PivotTable para agregados agrupados, ActivityFeed para un flujo en vivo de eventos, y Splitter para el diseño de dos paneles redimensionable que tan a menudo tiene que albergar todo lo anterior. Cada componente que sigue es una clase Delphi/C++Builder/.NET normal: se configuran sus propiedades, se llama a HTML y se obtiene de vuelta marcado Bootstrap 5. Sin framework de JavaScript, sin paso de compilación.

TreeGrid — tablas jerárquicas colapsables

TsgcHTMLComponent_TreeGrid renderiza una tabla Bootstrap cuyas filas se colapsan y expanden, construida a partir de simples pares de nodos Id / ParentId, la misma estructura que ya se usa para organigramas, árboles de categorías o datos anidados de listas de materiales. Se declara un TsgcHTMLTreeGridColumn por cada campo visible y luego se añaden nodos; la relación padre/hijo dibuja el árbol y las flechas de despliegue automáticamente.

uses
  sgcHTML_Component_TreeGrid;

var
  oTreeGrid: TsgcHTMLComponent_TreeGrid;
  oColumn: TsgcHTMLTreeGridColumn;
begin
  oTreeGrid := TsgcHTMLComponent_TreeGrid.Create(nil);
  try
    oTreeGrid.TreeGridID := 'orgchart';
    oTreeGrid.Sortable := True;

    oColumn := oTreeGrid.Columns.Add;
    oColumn.Caption := 'Department';
    oColumn.FieldName := 'Name';

    oColumn := oTreeGrid.Columns.Add;
    oColumn.Caption := 'Headcount';
    oColumn.FieldName := 'Headcount';
    oColumn.Align := tgaRight;

    oTreeGrid.AddNode('eng', '', ['Engineering', '42']);
    oTreeGrid.AddNode('eng-be', 'eng', ['Backend', '18']);
    oTreeGrid.AddNode('eng-fe', 'eng', ['Frontend', '15']);

    WebModule.Response := oTreeGrid.HTML;   // hierarchical table + scoped CSS + toggle script
  finally
    oTreeGrid.Free;
  end;
end;

// Or bind it straight to a self-referencing dataset (Id / ParentId columns):
oTreeGrid.LoadFromDataSet(qryDepartments, 'Id', 'ParentId');

Si los datos ya residen en una tabla autorreferenciada, se pueden omitir por completo las llamadas manuales a nodos y pasarle a LoadFromDataSet los nombres de campo padre e hijo: recorre el dataset y construye todo el árbol en una sola pasada.

PivotTable — agrupar, agregar, totalizar

TsgcHTMLComponent_PivotTable toma filas planas y produce una tabla agrupada y agregada con totales por fila, totales por columna y un gran total, el informe que de otro modo se construiría a mano en una hoja de cálculo. Se declaran RowFields y ColumnFields para agrupar, una o varias Measures para agregar (suma, promedio, conteo, mínimo, máximo), y una lista DataFields que indica qué columnas de origen leer realmente.

uses
  sgcHTML_Component_PivotTable;

var
  oPivot: TsgcHTMLComponent_PivotTable;
  oMeasure: TsgcHTMLPivotMeasure;
begin
  oPivot := TsgcHTMLComponent_PivotTable.Create(nil);
  try
    oPivot.Caption := 'Revenue by Region and Quarter';

    oPivot.RowFields.Add.FieldName := 'Region';
    oPivot.ColumnFields.Add.FieldName := 'Quarter';

    oMeasure := oPivot.Measures.Add;
    oMeasure.SourceField := 'Amount';
    oMeasure.Caption := 'Revenue';
    oMeasure.Aggregation := paSum;
    oMeasure.Format := '#,##0.00';

    oPivot.DataFields.Add('Region');
    oPivot.DataFields.Add('Quarter');
    oPivot.DataFields.Add('Amount');

    oPivot.AddRow(['EMEA', 'Q1', '12000']);
    oPivot.AddRow(['EMEA', 'Q2', '15500']);
    oPivot.AddRow(['APAC', 'Q1', '9800']);

    WebModule.Response := oPivot.HTML;   // aggregated table + row/column/grand totals
  finally
    oPivot.Free;
  end;
end;

// Or bind it straight to a dataset (reads only RowFields/ColumnFields/Measures fields):
oPivot.LoadFromDataSet(qrySales);

Todo lo que ocurre después de AddRow (o LoadFromDataSet) se calcula automáticamente: el componente agrupa las filas en bruto, aplica la agregación elegida en cada celda, y añade las filas y columnas de totales por fila, por columna y el gran total.

ActivityFeed — un flujo de eventos en vivo y con push

TsgcHTMLComponent_ActivityFeed renderiza una lista, con lo más reciente primero, de entradas de "quién hizo qué" con marcas de tiempo relativas ("hace 3 min", mantenidas en vivo del lado del cliente), y está diseñado desde el principio para recibir envíos por WebSockets, no solo para renderizarse una vez. MaxItems lo mantiene como un búfer circular: la entrada más antigua se descarta automáticamente al alcanzar el límite.

uses
  sgcHTML_Enums, sgcHTML_Component_ActivityFeed;

var
  oFeed: TsgcHTMLComponent_ActivityFeed;
begin
  oFeed := TsgcHTMLComponent_ActivityFeed.Create(nil);
  try
    oFeed.FeedID := 'timeline';
    oFeed.Title := 'Recent Activity';
    oFeed.MaxItems := 30;

    oFeed.AddActivity('Ana', 'closed', 'ticket #482', hcSuccess, 'bi bi-check-circle');
    oFeed.AddActivity('Marc', 'commented on', 'invoice #1190', hcPrimary);

    WebModule.Response := oFeed.HTML;   // feed list + scoped CSS + live relative-time script
  finally
    oFeed.Free;
  end;
end;

// Push a new item live to connected clients (out-of-band htmx fragment):
oFeed.AddActivity('Ana', 'approved', 'purchase order #77', hcSuccess);
Engine.BroadcastFragment(oFeed.GetLastItemFragmentHTML);

Esa última línea es la parte interesante: GetLastItemFragmentHTML renderiza solo la entrada más nueva como un fragmento htmx fuera de banda, y BroadcastFragment lo envía a todos los navegadores abiertos a través del servidor sgcWebSockets que ya se está ejecutando. Sin sondeo, sin una capa aparte de tiempo real que configurar.

Splitter — el contenedor de dos paneles que lo sostiene todo

TsgcHTMLComponent_Splitter es la pieza de diseño: dos paneles redimensionables con una barra de arrastre entre ellos, horizontal o vertical, con mínimos de tamaño fijo en cada lado para que ningún panel pueda arrastrarse hasta desaparecer. Es exactamente el contenedor que necesita una pantalla de TreeGrid más panel de detalle, o de lista de archivos más editor.

uses
  sgcHTML_Component_Splitter;

var
  oSplitter: TsgcHTMLComponent_Splitter;
begin
  oSplitter := TsgcHTMLComponent_Splitter.Create(nil);
  try
    oSplitter.SplitterID := 'workspace';
    oSplitter.Orientation := soHorizontal;
    oSplitter.InitialSplit := 30;
    oSplitter.MinSizeA := 180;
    oSplitter.MinSizeB := 320;
    oSplitter.GutterSize := 8;
    oSplitter.Height := '560px';
    oSplitter.PersistKey := 'workspace-split';

    oSplitter.AddPaneA('<div class="p-3"><h5>Files</h5>...</div>');
    oSplitter.AddPaneB('<div class="p-3"><h5>Editor</h5>...</div>');

    WebModule.Response := oSplitter.HTML;   // two panes + gutter + scoped CSS + drag script
  finally
    oSplitter.Free;
  end;
end;

PersistKey merece mención aparte: al establecerlo, la posición de la barra que un visitante arrastra se recuerda del lado del cliente y se restaura en su próxima visita, de modo que el diseño de un espacio de trabajo se mantiene fijo entre sesiones sin necesidad de escribir código de gestión de estado.

Pruébalos

Los cuatro se incluyen en la versión actual de sgcWebSockets, con documentación completa, referencias de propiedades clave y ejemplos de código Delphi/C++Builder/.NET en sus propias páginas: TreeGrid, PivotTable, ActivityFeed y Splitter. La matriz completa de funciones ahora lista 83 componentes en nueve familias, la próxima publicación de esta serie cubre los nuevos gráficos y códigos visuales.

¿Preguntas, comentarios o ayuda con la migración? Ponte en contacto, recibirás respuesta de las personas que escribieron el código.