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.
