HtmlForgeX.CanopyX
2.0.0
See the version list below for details.
dotnet add package HtmlForgeX.CanopyX --version 2.0.0
NuGet\Install-Package HtmlForgeX.CanopyX -Version 2.0.0
<PackageReference Include="HtmlForgeX.CanopyX" Version="2.0.0" />
<PackageVersion Include="HtmlForgeX.CanopyX" Version="2.0.0" />
<PackageReference Include="HtmlForgeX.CanopyX" />
paket add HtmlForgeX.CanopyX --version 2.0.0
#r "nuget: HtmlForgeX.CanopyX, 2.0.0"
#:package HtmlForgeX.CanopyX@2.0.0
#addin nuget:?package=HtmlForgeX.CanopyX&version=2.0.0
#tool nuget:?package=HtmlForgeX.CanopyX&version=2.0.0
HtmlForgeX.CanopyX
HtmlForgeX.CanopyX hosts CanopyX tree grids and flat data grids in HtmlForgeX documents and
reports. CanopyX owns the view (columns, records, nodes, lazy children, search, detail rows, keyboard and accessibility
behaviour); this package places a view in the page and makes it part of the document.
using CanopyX;
using HtmlForgeX;
var view = new CanopyTreeView("gpo", "Group Policy objects");
view.Columns.Add(new CanopyColumn("name", "Name") { Sizing = CanopyColumnSizing.Preferred(320), Sortable = true });
view.Columns.Add(new CanopyColumn("links", "Links") { Kind = CanopyColumnKind.Number, Sortable = true });
var domain = new CanopyNode("corp.example", "corp.example");
var policy = new CanopyNode("corp.example/default-domain-policy", "Default Domain Policy");
policy.Cells["links"] = 1;
policy.Badges.Add(new CanopyBadge("changed", CanopyTone.Warning));
domain.Children.Add(policy);
view.Roots.Add(domain);
using var document = new Document();
document.Body.CanopyTreeGrid(view, grid => grid.Height(520));
For a flat inventory, map a typed report dataset into a grid definition:
var dataset = ReportDataset.Create("inventory", records, columns => columns
.Text("id", "Record", record => record.Id, column => column.Key())
.Number("size", "Size", record => record.Size));
var view = CanopyReportGrid.FromDataset(dataset, "inventory-grid");
document.Body.CanopyDataGrid(view, grid => grid.Height(480));
// Prefer grouped settings when configuring several options.
document.Body.CanopyDataGrid(view, grid => grid.Settings(s => s
.Height(480).Density(CanopyGridDensity.Compact).StaticSnapshot(null, maxRows: 100).End()));
For a bounded summary panel, grid.Settings(s => s.Summary()) draws the grid without toolbar, column chooser, result line, status bar, paging footer, row selection, column menus, record drawer or frame, and fits its height to the rows; Summary(sorting: true) keeps sortable headers. CanopyStatusStrip.Period(...) and CanopyStatusStrip.Cell(...) fill CanopyX's status-strip presenter from report states (healthy quiet, degraded and down in their tones, no data neutral).
The key must be unique and nonempty. Raw numbers and dates remain typed; display text and report status tones are retained. The flat grid provides typed filters, stable sorting, selection, saved views, copying and explicit filtered, selected or all-result export scope. Its browser host hooks govern record details and Office exports. A host-backed definition supplies its data source before mounting and can pass an explicit accepted page to StaticSnapshot for script-free output.
An existing materialized report explorer can use the shared viewport while retaining HtmlForgeX's report controls and drawer:
document.Body.CanopyDataExplorer(dataset, explorer => explorer
.Title("Inventory")
.AddView("All records", view => view.Default())
.Settings(settings => settings.Height(480).Exports(DataExplorerExports.All)));
This host retains report views, quick filters, column formatting, related records and report definitions. Its export menu distinguishes filtered results, selected rows and all results. Column order, labels and sorting come from the accepted query; selected rows include identities outside the current filter. CSV and record copying work independently of virtual rows. Copying includes headers, quotes tabs and line breaks, protects text formulas and accepts at most 10,000 rows. DataExplorerExports.All enables CSV, record copying and definition copying. Office files use Settings(settings => settings.HostExports("/api/exports/{id}", "xlsx")) on an HTTP(S) host; the host writes the file.
The explorer requires a unique, nonempty text or category key and orders ties by that key using ordinal comparison. Widths and pins the reader chose and stable selection survive query changes; columns on automatic sizing fit the new result again. Remote report sources continue to use DataExplorer; the optional CanopyX explorer accepts complete materialized datasets. A host removing a mounted explorer calls its browser controller's destroy() to release the viewport and drawer ownership.
Services replay an exact selection with new ReportQuery().SelectKeys("record-A", "record-a"). Keys preserve case and whitespace, and an empty selection yields no records. The query's filters, sorting and paging then apply to those records. A source advertises SupportsRowKeys only when it enforces this identity contract; materialized datasets support it, while the database bridge rejects it before executing SQL. Host query limits bound the selected-key count as well as the request size. Ordinary category filters retain their existing comparison behavior.
Configure a date-time column with c => c.ShowSeconds() when readers need precise event times. This setting
includes seconds in cells and searches; typed instants, sorting, filters and export precision stay the same.
In CanopyDataExplorer, .LocalTime().ShowSeconds() uses the reader's browser time zone and follows the report's
local/UTC switch. CanopyReportGrid.FromDataset(...) instead supplies per-column display text: its local text uses
the generating machine's zone because CanopyX has a view-wide time zone. Changing the native time-zone control
does not rewrite that snapshot text. Author CanopyDataGridView without text overrides when native formatting is needed.
CanopyX owns cell presentation, row density, column menus and filter popovers. The bridge maps the HtmlForgeX
Graphite tokens onto the native grid and uses native link, identifier, long-text, boolean and status presenters.
A row click opens the report's record (its row target, its report entity with RowEntity(type), or its drawer); without
those it follows the row header's link when that column is a link (Enter does the same), otherwise it only focuses the row. A plain click never selects: row checkboxes, Space and
the header's selection menu do. Enter, a double-click or the native Details action also open the record. The Grid
views menu saves native layouts. The report's Save view entry saves replayable report definitions. Export
offers the export scopes, report definitions and host files.
Settings(s => s.Labels.GridExport = "Export grid") (used when the explorer has no exports of its own, so the grid
keeps its Export) and Labels.GridViews localize those menu names. Explorer
labels for columns, filters, details and export scopes are also passed to the native controls; advanced native
editor labels keep CanopyX's defaults.
Use Settings(...) on both hosts for sizing and advanced options. CanopyDataExplorer reuses
DataExplorerSettings; CanopyDataGridSettings wraps height, window fit (FitToViewport(minimum, maximum)), native density, row action (RowAction(CanopyGridRowAction.Details)),
auto height (AutoHeight(false) keeps a fixed-height panel), row accents (RowAccents()), summary mode and static
snapshots and returns the grid through End(). Existing root methods remain compatible. Empty states use Labels.Empty for an empty view,
Labels.EmptyFiltered for no matches, and Labels.ClearFilters for the scoped clear action. Clearing retains the
selected report view. Entity panels and tab panels initialize when shown; removing their roots releases their
grids automatically. A new grid accepts its definition and first page before the old controller is destroyed.
Native column conditions become an exact-key snapshot in report exports and saved report definitions. CSV can cover the materialized result; service replay of those snapshots has the report protocol's 10,000-key limit. All-matching selection is captured against the accepted native query, with exclusions. Explicit selections survive report query changes; an all-matching selection is retired when that query changes. An explorer never hides its last visible column.
The data-explorer example builds one ReportPackage for single-file and bundle delivery, including portable
compressed datasets and related certificate observations. Flat-grid runtimes include the matching native editor
assets, so column menus, filter popovers and long-text dialogs also work without a network connection.
Existing DataTables tables can switch to CanopyX grids without changing the code that builds them:
document.UseCanopyXTables(options => options.OnDiagnostic(d => log.WriteLine(d.ToString())));
Each table is bridged when the grid can carry everything it uses (highlight rules become row and cell tones, exports
become CanopyX exports, responsive priorities become column layout); otherwise it keeps DataTables and
document.GetCanopyXTableDiagnostics() says why. See Docs/COMPONENT_REFERENCE_DATATABLES_CANOPYX.md.
What the bridge adds:
- Assets follow the document. The CanopyX runtime and stylesheet are inlined in a single file, written next to
the page when the document saves its assets as files, and kept in a report bundle, always under CanopyX's
content-hashed names (
canopyx.<hash>.js), so a page, its runtime and its tree data come from one CanopyX build. They load once per page however many grids it holds. Checkbox, editable, movable, and context-action views select the complete interactive entry point for every grid on the page. - Theme. The grid takes its colours from the Evotec design tokens, so it matches the report in light and dark and follows the reader's theme choice.
- Without script. A static outline of the tree (up to 200 rows by default, with a note about the rest) is shown
where script cannot run. It is written in the light token colours (what email, Word and print keep) and follows
the page's theme on screen through the same variables as the grid.
Settings(s => s.StaticOutline(CanopyStaticOutline.Only))renders only the outline, for documents that are printed, mailed or converted. - Status.
status.ToCanopyTone()gives a badge or a cell the tone of a report status (critical,high,pass,not-evaluated,downand so on); the grid and the static outline colour it from the design tokens. - Time. Date columns follow the report's choice of the reader's time zone or UTC.
- Language.
Settings(s => s.Labels(l => ...))sets the texts CanopyX writes itself (CanopyLabels: search box, loading and error rows, announcements, the outline's notes). They are the view's labels, so the grid and the outline are translated together;Settings(s => s.FullReport(address))links a shortened outline to the full report. - Entity addresses.
node.ReportEntity("ou", key)marks a row as a report entity, andSettings(s => s.EntityLinks())replaces the grid's own link scheme with the report's: selecting the row writes#ou/<key>, and that address shows the page of the grid and opens the row. The bridge registers the scheme withCanopyX.configure(formatLink,parseLink) before it mounts the view. - Filters in the report's style.
Settings(s => s.FilterBar(b => b.Choice("Changed", "Any time", "day", "week")))draws the view's filters (CanopyFilter) as report chips above the grid instead of the grid's own switches: one switch per filter, and one choice for filters that are alternatives. The grid keeps the state (updateActiveFilters,getActiveFilters), and the chips follow itscanopyx:filterevent, also when the grid turns the filters off to show a row a search or an address leads to. A choice is a radio group (one tab stop, arrow keys); switching its option replaces its alternatives in one transaction and retains unrelated pending selections on query-backed data. Failed queries leave the chips on the accepted state. Rendering the bar leaves the view's filter definitions intact. A filter the author already hid keeps the author's own control. - Large trees.
Settings(s => s.Data(CanopyTreeGridData.Sidecar))moves the nodes into small script files next to the page when the document is saved with its assets as files; the page then loads children as the reader opens nodes, also from a file share.
page.CanopyTreeGrid(view, grid => grid
.HeightOfWindow(70)
.Settings(s => s
.Frame(false)
.Data(CanopyTreeGridData.Sidecar)
.StaticOutline(CanopyStaticOutline.WithoutScript, maxRows: 100)
.Labels(l => l.OverflowNote = "Pokazano {shown} z {total} wierszy.")));
This package replaces the jQuery FancyTree wrapper (FancyTree, FancyTreeNode) that HtmlForgeX shipped before.
See MIGRATION.md in the repository for the mapping.
Column layout
CanopyX sizes flat-grid columns to their content, fills the width and decides what gives way when it runs short (see
CanopyX docs/column-layout.md). HtmlForgeX passes the intent through under CanopyX's own names and does not size
columns itself:
| Host | Where the options come from |
|---|---|
CanopyDataGrid |
The hosted view's CanopyColumn options: ResponsivePriority, Pinned, RowHeader and Sizing. Choose CanopyColumnSizing.Content(...), Preferred(width, ...), Flex(weight, ...) or Fixed(width, minimum, maximum) to declare width, bounds and automatic measurement. The view also declares RowAction, AutoHeight and RowAccents, available through Settings(...). |
CanopyReportGrid.FromDataset |
Each ReportDatasetColumn's layout (c => c.Priority(...), ResponsivePriority(n), Pinned(...), RowHeader(), MinimumWidth(px), MaximumWidth(px), Fixed(), Flex(w), AutoSize(...)), mapped onto CanopyColumn.Sizing, ResponsivePriority and pins. Width(px) supplies the preferred or fixed width. |
CanopyDataExplorer |
The same ReportDatasetColumn layout, written into the page as the column's layout and forwarded to each grid the explorer creates. |
| DataTables bridge | DataTables responsivePriority becomes CanopyX's numeric priority (the same scale: 0 and 1 essential, 10000 and above optional); in a Responsive table a column without one takes DataTables' default of 10000, except the identity column and in summary grids, and the all class keeps a column essential. The first shown column is the row header. FixedColumns edges become pins (one left column by default, as in DataTables; leftColumns: 0 unpins the identity column). A DataTables width is the preferred width. |
CanopyTreeGrid |
Nothing to forward: CanopyX writes these options for flat grids only; trees keep PinTreeColumn. |
Report-layer defaults, each overridable:
- Row header. The column marked
RowHeader(), otherwise the dataset's key column (unless it saysRowHeader(false)), otherwise the first shown column, is the identity column: essential and pinned left. A hidden column is never the row header, because CanopyX's own fallback (the first column) could be one.Pinned(ReportColumnPin.None)or a priority on it changes that; on a replacedCanopyReportGridview setCanopyColumn.RowHeaderdirectly. - Row action.
CanopyReportGridviews open the record details on a row click, or follow the row header's link when it is a link column; the explorer opens its row target, its report entity (RowEntity) or its drawer, and follows the row header's link when it opens none of them. A link-text row header (c.Link(targetColumn)) shows the row's name and links to the target column's address (CanopyXhrefColumn); an address that is a report entity opens the shell drawer instead of navigating to the bare fragment. Summary grids open nothing. Bridged tables open the record details only in Responsive tables (not summaries), where CanopyX's drawer replaces DataTables' child rows. Change it withview.RowActionorSettings(s => s.RowAction(...)). - Row accents stay off. The bridge turns them on only for a table whose highlight rules colour whole rows, the
author's explicit request;
Settings(s => s.RowAccents())turns them on for any grid. - Height. CanopyX's auto height is kept: a grid whose rows all fit is as tall as its rows. A fixed-height explorer
(
FixedHeight()) turns it off; aCanopyDataGridusesSettings(s => s.AutoHeight(false)).FitToViewport(minimum, maximum)on the explorer settings or the grid settings makes the bound the room down to the window's bottom (20 px above it, the rule of the report layout's fitted lists) instead of a fixed number of pixels. - Quiet tones. A toned column's enum presenter always lists CanopyX's neutral tone in
quietTones, so values without a tone are plain muted text, never neutral chips.ReportDatasetColumn.QuietTones(...)andDataExplorerSettings.QuietTones(...)add the report quiet tones:Successbecomespass,upandsuccess,Infobecomesinfoandmaintenance.
What CanopyX does not offer yet, and HtmlForgeX works around:
- Enter on a row whose
rowActionislinkopens the details hook instead of following the row link. The explorer's details hook follows the link itself; a plainCanopyDataGridneeds CanopyX to make Enter run the row action (alinkrow action following the first row link on Enter, with Ctrl+Enter kept for details). - An enum or state presenter has no way to draw a value without a tone in the normal text colour: quiet tones are muted
text, so untoned values are muted too. A presenter option such as
untoned: "text"would draw them as plain cell text. Quiet text also starts at the cell edge while pills are inset by their padding, so quiet and chipped values of one column do not line up; the quiet text could take the pill's inline padding. - Link cells carry no hook for a host's link handling (an attribute or an
onLinkcallback), so HtmlForgeX finds entity links by their resolved address in a document-level click handler.
Embedded grids inside a report shell take the shell's scrollbar tokens (--hfx-scrollbar-* set as CanopyX's
--cx-scrollbar-* on the grid host), so light, dark and a runtime theme switch match the report.
Report explorer toolbar
A CanopyDataExplorer has one toolbar, built with CanopyX's toolbar hooks (reporting runtime) rather than by moving
grid elements:
- Slots. The explorer's quick views keep their own row above the grid. Its search sits in the grid toolbar's
startslot, its active filter chips inafterViews(after the grid's Views menu) and its row count and Export inend, the start of the right-hand group. CanopyX lays the slots out with its own controls, keeps them through re-renders and the phone layout, and each replacement grid takes the explorer's controls into its own slots. - Switches. The grid is created with
toolbar: { count: false }, plusexport: falsewhen the explorer offers its own Export, so every control appears once. The explorer's Export also offers report definitions and host-rendered files; without explorer exports the grid's Export (Labels.GridExport) stays. - More menu. Save view is a content entry of the grid's more (⋯) menu (
actionswithrender): it opens a name field and a Save button styled as grid controls; Escape folds it back, saving closes the menu. Reset is an entry that runs (actionswithrun) and returns to the current view without the reader's search and filters. - Phone. On a phone (CanopyX's
cx-phone, a grid 640 px wide or less) the toolbar follows CanopyX's phone pattern: the search takes its own row, and Filters, Columns, the row count and the more menu share one compact row. The count shows its short form (data-short:52, or12 / 52when filters narrow the rows) while its text keeps the full sentence for assistive technology, and the explorer's Export moves into the more menu as its first entry, as CanopyX moves its own Views and Export there. Export then opens as a sheet of its own that covers the more sheet (one drag handle, a Back action,Labels.Back); Escape or Back returns to the more sheet with focus on its Export entry, and the covered entries are inert meanwhile. Widening the grid brings Export back to the toolbar as a popover. - Layered menus. The grid's menus open in the browser's top layer. The explorer's Export panel does the same: the
CanopyX bridge sets
explorer.layeredMenus, and the explorer then opens its menu panels as manual popovers placed by their button and kept inside the viewport (hfx-data-explorer.menus.js), so a one-row grid or a host withoverflow: hiddendoes not cut them off. Browsers without the popover API get the same placement with fixed positioning, which escapesoverflow: hiddenbut not an ancestor with atransform,filterorcontain.
The Columns menu is CanopyX's (find, drag or Alt+Up/Down to reorder, Show all, Reset columns; it may show "Loading…"
while its editor chunk arrives), so the explorer's headers carry no move handles. Which columns are out of view, and
why, comes from CanopyX's getColumnLayout() (visible and hiddenReason: user, priority or phone); the
report's own columns are the ones the reader did not hide, so a column that gives way to the width or the phone layout
stays in the report's exports and saved views.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- CanopyX (>= 0.2.1)
- CanopyX.Html (>= 0.2.1)
- HtmlForgeX (>= 2.0.0)
-
.NETStandard 2.0
- CanopyX (>= 0.2.1)
- CanopyX.Html (>= 0.2.1)
- HtmlForgeX (>= 2.0.0)
-
net10.0
- CanopyX (>= 0.2.1)
- CanopyX.Html (>= 0.2.1)
- HtmlForgeX (>= 2.0.0)
-
net8.0
- CanopyX (>= 0.2.1)
- CanopyX.Html (>= 0.2.1)
- HtmlForgeX (>= 2.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.