O sgcHTML acaba de crescer em 25 componentes, e este é o primeiro de cinco posts que os percorrem por tema. Começamos com quatro componentes construídos em torno de dados estruturados e em constante mudança: TreeGrid para linhas hierárquicas, PivotTable para agregados agrupados, ActivityFeed para um fluxo ao vivo de eventos, e Splitter para o layout redimensionável de dois painéis que tantas vezes precisa conter tudo isso. Cada componente abaixo é uma classe Delphi/C++Builder/.NET comum: defina suas propriedades, chame HTML, obtenha de volta o markup Bootstrap 5. Sem framework JavaScript, sem etapa de build.
TreeGrid — tabelas hierárquicas recolhíveis
TsgcHTMLComponent_TreeGrid renderiza uma tabela Bootstrap cujas linhas se recolhem e se expandem, construída a partir de simples pares de nós Id / ParentId — a mesma estrutura que você já usa para organogramas, árvores de categorias ou dados de lista de materiais aninhados. Declare uma TsgcHTMLTreeGridColumn por campo visível e, em seguida, adicione nós; o relacionamento pai/filho desenha a árvore e as setas de alternância para você.
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');
Se seus dados já residem em uma tabela autorreferenciada, dispense as chamadas manuais de nós e passe para LoadFromDataSet os nomes dos campos pai e filho — ele percorre o dataset e constrói toda a árvore em uma única passagem.
PivotTable — agrupar, agregar, totalizar
TsgcHTMLComponent_PivotTable recebe linhas simples e produz uma tabela agrupada e agregada com totais de linha, totais de coluna e um total geral, o relatório que você de outra forma montaria manualmente em uma planilha. Você declara RowFields e ColumnFields para agrupar, uma ou mais Measures para agregar (soma, média, contagem, mínimo, máximo), e uma lista DataFields informando quais colunas de origem devem realmente ser lidas.
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);
Tudo o que vem depois de AddRow (ou LoadFromDataSet) é calculado para você: o componente agrupa as linhas brutas, aplica a agregação escolhida em cada célula e anexa automaticamente as linhas e colunas de total de linha/coluna/geral.
ActivityFeed — um fluxo de eventos ao vivo e alimentável
TsgcHTMLComponent_ActivityFeed renderiza uma lista, do mais recente para o mais antigo, de entradas do tipo "quem fez o quê" com carimbos de data/hora relativos ("há 3 min", mantidos ao vivo no lado do cliente), e foi projetado desde o início para receber envios via WebSockets, não apenas para ser renderizado uma vez. MaxItems o mantém como um buffer circular — a entrada mais antiga é descartada automaticamente assim que o limite é atingido.
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);
Essa última linha é a parte interessante: GetLastItemFragmentHTML renderiza apenas a entrada mais recente como um fragmento htmx out-of-band, e BroadcastFragment o envia para todos os navegadores abertos por meio do servidor sgcWebSockets que você já está executando. Sem polling, sem uma camada separada de tempo real para configurar.
Splitter — o shell de dois painéis que sustenta tudo
TsgcHTMLComponent_Splitter é a peça de layout: dois painéis redimensionáveis com uma calha arrastável entre eles, horizontal ou vertical, com mínimos de piso em pixels em cada lado para que nenhum painel possa ser arrastado até desaparecer. É exatamente o shell de que uma tela de TreeGrid mais painel de detalhes, ou de lista de arquivos mais editor, precisa.
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 destaque: ao defini-lo, a posição da calha para onde um visitante a arrasta é memorizada no lado do cliente e restaurada na próxima visita, de modo que o layout de um workspace permanece o mesmo entre sessões sem que você precise escrever nenhum código de gerenciamento de estado.
Experimente
Os quatro já estão disponíveis na versão atual do sgcWebSockets, com documentação completa, referências das principais propriedades e exemplos de código em Delphi/C++Builder/.NET em suas próprias páginas: TreeGrid, PivotTable, ActivityFeed e Splitter. A matriz completa de recursos agora lista 83 componentes em nove famílias — o próximo post desta série cobre os novos gráficos e códigos visuais.
Dúvidas, feedback ou ajuda com migração? Entre em contato — você receberá uma resposta das pessoas que escreveram o código.
