Cztery nowe komponenty sgcHTML do danych strukturalnych: TreeGrid, PivotTable, ActivityFeed i Splitter | eSeGeCe Blog

Cztery nowe komponenty sgcHTML do danych strukturalnych: TreeGrid, PivotTable, ActivityFeed i Splitter

· Komponenty

sgcHTML urósł właśnie o 25 komponentów, a to pierwszy z pięciu wpisów omawiających je tematycznie. Zaczynamy od czterech komponentów zbudowanych wokół danych strukturalnych i zmieniających się w czasie: TreeGrid dla wierszy hierarchicznych, PivotTable dla zgrupowanych agregatów, ActivityFeed dla strumienia zdarzeń na żywo oraz Splitter dla elastycznego układu dwupanelowego, który tak często musi pomieścić wszystkie powyższe. Każdy z poniższych komponentów to zwykła klasa Delphi/C++Builder/.NET: ustawiasz jej właściwości, wywołujesz HTML i otrzymujesz gotowy znacznik Bootstrap 5. Bez frameworka JavaScript, bez etapu budowania.

TreeGrid — zwijane tabele hierarchiczne

TsgcHTMLComponent_TreeGrid renderuje tabelę Bootstrap, której wiersze można zwijać i rozwijać, zbudowaną na podstawie prostych par węzłów Id / ParentId — dokładnie takich, jakich już używasz do schematów organizacyjnych, drzew kategorii czy zagnieżdżonych struktur materiałowych. Zadeklaruj TsgcHTMLTreeGridColumn dla każdego widocznego pola, a następnie dodaj węzły; relacja rodzic/dziecko sama rysuje drzewo i strzałki rozwijania.

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');

Jeśli Twoje dane już znajdują się w tabeli samoreferencyjnej, możesz całkowicie pominąć ręczne wywołania węzłów i przekazać do LoadFromDataSet nazwy pól rodzica i dziecka — metoda ta przechodzi przez zbiór danych i buduje całe drzewo za jednym razem.

PivotTable — grupowanie, agregacja, sumy

TsgcHTMLComponent_PivotTable przyjmuje płaskie wiersze i tworzy z nich pogrupowaną, zagregowaną tabelę z sumami wierszy, sumami kolumn i sumą końcową, czyli raport, który inaczej musiałbyś budować ręcznie w arkuszu kalkulacyjnym. Deklarujesz RowFields i ColumnFields, według których grupować dane, jedną lub więcej pozycji Measures do agregacji (suma, średnia, liczba, minimum, maksimum), oraz listę DataFields określającą, które kolumny źródłowe mają być faktycznie odczytywane.

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);

Wszystko, co następuje po AddRow (lub LoadFromDataSet), jest obliczane automatycznie: komponent grupuje surowe wiersze, stosuje wybraną agregację w każdej komórce oraz automatycznie dodaje wiersze i kolumny sum częściowych i sumę końcową.

ActivityFeed — strumień zdarzeń na żywo, zasilany przez push

TsgcHTMLComponent_ActivityFeed renderuje listę wpisów typu "kto co zrobił", od najnowszych do najstarszych, ze znacznikami czasu względnego ("3 min temu", aktualizowanymi na żywo po stronie klienta), i od samego początku został zaprojektowany tak, by dało się do niego wypychać dane przez WebSockets, a nie tylko renderować go raz. MaxItems sprawia, że działa jak bufor cykliczny — po osiągnięciu limitu najstarszy wpis jest automatycznie usuwany.

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);

Ta ostatnia linia jest najciekawsza: GetLastItemFragmentHTML renderuje tylko najnowszy wpis jako fragment htmx typu out-of-band, a BroadcastFragment wysyła go do każdej otwartej przeglądarki przez serwer sgcWebSockets, który już masz uruchomiony. Bez odpytywania, bez konieczności podłączania osobnej warstwy czasu rzeczywistego.

Splitter — dwupanelowa powłoka, która mieści wszystko

TsgcHTMLComponent_Splitter to element układu: dwa panele o zmiennym rozmiarze z przeciąganym uchwytem między nimi, poziomo lub pionowo, z minimalnymi wartościami w pikselach dla każdej strony, dzięki czemu żaden panel nie da się przeciągnąć do zera. To dokładnie taka powłoka, jakiej potrzebuje ekran typu TreeGrid plus panel szczegółów albo lista plików plus edytor.

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 jest wart osobnej wzmianki: ustaw tę właściwość, a pozycja uchwytu, do której przeciągnie go odwiedzający, zostanie zapamiętana po stronie klienta i przywrócona przy jego kolejnej wizycie, dzięki czemu układ przestrzeni roboczej pozostaje niezmienny między sesjami bez pisania jakiegokolwiek kodu zarządzania stanem.

Wypróbuj je

Wszystkie cztery komponenty są dostępne w bieżącym wydaniu sgcWebSockets, z pełną dokumentacją, opisem kluczowych właściwości oraz przykładami kodu Delphi/C++Builder/.NET na własnych stronach: TreeGrid, PivotTable, ActivityFeed i Splitter. Pełna matryca funkcji zawiera teraz 83 komponenty w dziewięciu rodzinach — kolejny wpis z tej serii omówi nowe wykresy i kody wizualne.

Masz pytania, uwagi lub potrzebujesz pomocy w migracji? Skontaktuj się z nami — odpowie Ci ktoś, kto naprawdę napisał ten kod.