@evotecit/canopyx 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CanopyX.Html/Assets/canopyx-filter-editor.css +135 -0
  2. package/CanopyX.Html/Assets/canopyx-filter-editor.js +61 -0
  3. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +1035 -0
  4. package/CanopyX.Html/Assets/canopyx-grid.css +554 -0
  5. package/CanopyX.Html/Assets/canopyx-grid.js +1033 -0
  6. package/CanopyX.Html/Assets/canopyx-interactive.css +351 -0
  7. package/CanopyX.Html/Assets/canopyx-interactive.js +664 -0
  8. package/CanopyX.Html/Assets/canopyx-labels-de.js +225 -0
  9. package/CanopyX.Html/Assets/canopyx-labels-es.js +225 -0
  10. package/CanopyX.Html/Assets/canopyx-labels-pl.js +225 -0
  11. package/CanopyX.Html/Assets/canopyx-module.mjs +665 -0
  12. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +1278 -0
  13. package/CanopyX.Html/Assets/canopyx-reporting.css +604 -0
  14. package/CanopyX.Html/Assets/canopyx-reporting.js +1276 -0
  15. package/CanopyX.Html/Assets/canopyx.css +325 -0
  16. package/CanopyX.Html/Assets/canopyx.js +534 -0
  17. package/JavaScript/grid.d.ts +591 -0
  18. package/JavaScript/hosts.d.ts +109 -0
  19. package/JavaScript/index.d.ts +85 -0
  20. package/JavaScript/locales/de.d.ts +6 -0
  21. package/JavaScript/locales/de.mjs +223 -0
  22. package/JavaScript/locales/es.d.ts +6 -0
  23. package/JavaScript/locales/es.mjs +223 -0
  24. package/JavaScript/locales/pl.d.ts +6 -0
  25. package/JavaScript/locales/pl.mjs +223 -0
  26. package/JavaScript/protocol.d.ts +229 -0
  27. package/LICENSE +21 -0
  28. package/README.md +198 -2
  29. package/README.nuget.md +131 -0
  30. package/docs/images/static-outline.png +0 -0
  31. package/docs/images/tree-grid-dark.png +0 -0
  32. package/docs/images/tree-grid-light.png +0 -0
  33. package/package.json +82 -4
@@ -0,0 +1,223 @@
1
+ // Generated from CanopyX/Views/Locales/labels.json by Build/Sync-Assets.ps1.
2
+ const labels = Object.freeze({
3
+ searchPlaceholder: "Szukaj",
4
+ searchResults: "Wyniki wyszukiwania",
5
+ searchResultCount: "Wyniki: {count}",
6
+ searchResultCountOfTotal: "Wyniki: {count} z {total}",
7
+ searchResultCountOfMany: "Wyświetlone wyniki: {count}",
8
+ searchNoResults: "Brak wyników",
9
+ searchTooShort: "Wpisz co najmniej {min} znaków",
10
+ searchWordTooShort: "Wpisz słowo o długości co najmniej {min} znaków",
11
+ searchFailed: "Nie udało się wyszukać: {error}",
12
+ columns: "Kolumny",
13
+ gridFilters: "Filtry",
14
+ gridFitContent: "Dopasuj do zawartości",
15
+ gridUnpin: "Odepnij",
16
+ gridWhere: "Gdzie",
17
+ gridComplexFilter: "Ta kolumna należy do mieszanej grupy warunków. Edytuj ją w Filtrach.",
18
+ gridClearColumnFilter: "Wyczyść filtr",
19
+ gridSelectionAggregate: "Liczba: {count} · Suma: {sum} · Średnia: {average} · Minimum: {minimum} · Maksimum: {maximum}",
20
+ gridConditionValue: "Wartość",
21
+ gridConditionUpperValue: "Górna wartość",
22
+ gridDeleteCondition: "Usuń warunek",
23
+ gridShowRowsWhere: "Pokaż wiersze, dla których",
24
+ gridOfTheseAreTrue: "z tych warunków są spełnione",
25
+ gridOfThese: "z tych warunków",
26
+ gridGroupCount: "Grupy: {count}",
27
+ gridMore: "Więcej",
28
+ gridDone: "Gotowe",
29
+ gridClearAll: "Wyczyść wszystko",
30
+ gridRows: "wierszy",
31
+ gridResultNoun: "{count} z {total} {noun}",
32
+ gridSearchNoun: "Szukaj: {noun}",
33
+ gridChart: "Wykres",
34
+ gridColumnMenu: "Menu kolumny {column}",
35
+ gridSortAscending: "Sortuj rosnąco",
36
+ gridSortDescending: "Sortuj malejąco",
37
+ gridClearSort: "Usuń sortowanie",
38
+ gridFilterColumn: "Filtruj…",
39
+ gridHideColumn: "Ukryj kolumnę",
40
+ gridGroupedBy: "Grupowanie",
41
+ gridRemoveGrouping: "Usuń grupowanie",
42
+ gridSelectAllValues: "Zaznacz wszystkie wartości",
43
+ gridFilterValues: "Szukaj wartości",
44
+ gridAddCondition: "+ Warunek",
45
+ gridAddGroup: "+ Grupa",
46
+ gridAnd: "i",
47
+ gridOr: "lub",
48
+ gridEditAsText: "Edytuj jako tekst",
49
+ gridFilterText: "Wyrażenie filtru",
50
+ gridDeepFilters: "Zapisany filtr ma więcej niż dwa poziomy zagnieżdżenia.",
51
+ gridToday: "Dzisiaj",
52
+ gridYesterday: "Wczoraj",
53
+ gridLastSevenDays: "Ostatnie 7 dni",
54
+ gridLastThirtyDays: "Ostatnie 30 dni",
55
+ gridThisMonth: "Ten miesiąc",
56
+ gridDateRange: "Zakres",
57
+ gridEmpty: "Brak: {noun}",
58
+ gridEmptyReason: "Nie ma rekordów do wyświetlenia.",
59
+ gridNoMatches: "Brak pasujących: {noun}",
60
+ gridNoMatchesReason: "Wyszukiwanie: {search}. Aktywne filtry: {count}.",
61
+ gridRetry: "Ponów",
62
+ gridLoadFailed: "Nie udało się wczytać rekordów",
63
+ gridAll: "Wszystkie",
64
+ gridPageOf: "{page} z {pages}",
65
+ gridStatusCount: "{count} {noun} · zaznaczono {selected}",
66
+ gridDetailsSection: "Szczegóły",
67
+ gridPreviousRecord: "Poprzedni rekord",
68
+ gridNextRecord: "Następny rekord",
69
+ gridShowMore: "Pokaż więcej",
70
+ gridNoData: "Brak danych",
71
+ gridPeriodRange: "{from}–{to}: {state}",
72
+ gridHealthy: "Prawidłowy",
73
+ gridResultCount: "Wiersze: {count} z {total}",
74
+ gridUnknownCount: "Liczba wyników jest niedostępna",
75
+ gridMatchedCount: "Pasujące wiersze: {count}",
76
+ gridCursorRange: "Wiersze {first}–{last}",
77
+ gridPaging: "Strony wyników",
78
+ gridPageRange: "Wiersze {first}–{last} z {count}",
79
+ gridPageNumber: "Strona",
80
+ gridPageLength: "Wiersze na stronę",
81
+ gridFirstPage: "Pierwsza strona",
82
+ gridPreviousPage: "Poprzednia strona",
83
+ gridNextPage: "Następna strona",
84
+ gridLastPage: "Ostatnia strona",
85
+ gridClearQuery: "Wyczyść wyszukiwanie i filtry",
86
+ gridRemoveSearch: "Wyczyść wyszukiwanie: {search}",
87
+ gridGroupedFilters: "Grupy filtrów (warunki: {count})",
88
+ gridRemoveGroup: "Wyczyść grupy filtrów",
89
+ gridRemoveCondition: "Usuń warunek: {condition}",
90
+ gridExport: "Eksportuj",
91
+ gridFilteredResults: "Przefiltrowane wyniki",
92
+ gridSelectedRows: "Zaznaczone wiersze",
93
+ gridAllResults: "Wszystkie wyniki",
94
+ gridDownloadCsv: "Pobierz plik CSV",
95
+ gridCopy: "Kopiuj",
96
+ gridDetails: "Szczegóły rekordu",
97
+ gridColumn: "Kolumna",
98
+ gridOperator: "Warunek",
99
+ gridValue: "Wartość (daty w UTC)",
100
+ gridValueType: "Typ wartości źródłowej",
101
+ gridText: "Tekst",
102
+ gridBoolean: "Wartość logiczna",
103
+ gridAddFilter: "Dodaj filtr",
104
+ gridRemoveFilter: "Usuń filtr kolumny {column}",
105
+ gridApply: "Zastosuj",
106
+ gridClearFilters: "Wyczyść wszystkie filtry",
107
+ gridMoveEarlier: "Przenieś kolumnę {column} w lewo",
108
+ gridMoveLater: "Przenieś kolumnę {column} w prawo",
109
+ gridViews: "Widoki",
110
+ gridViewName: "Nazwa widoku",
111
+ gridSaveView: "Zapisz widok",
112
+ gridRemoveView: "Usuń widok",
113
+ gridResetView: "Resetuj widok",
114
+ gridChooseView: "Wybierz zapisany widok",
115
+ gridInvalidFilter: "Wprowadź prawidłową wartość filtru.",
116
+ gridBetween: "między (włącznie)",
117
+ gridRangeEnd: "Wartość końcowa (daty w UTC)",
118
+ gridInvalidRange: "Wprowadź wartość końcową nie mniejszą niż początkowa.",
119
+ gridSuggestedCategory: "Sugerowana kategoria",
120
+ gridChooseCategory: "Wybierz kategorię",
121
+ gridBooleanValue: "Wartość logiczna",
122
+ gridTrue: "Prawda",
123
+ gridFalse: "Fałsz",
124
+ gridHeaderFilter: "Filtruj kolumnę {column}",
125
+ gridExpandCell: "{column}: pokaż pełny tekst",
126
+ gridCloseCell: "Zamknij pełny tekst",
127
+ gridMoveColumn: "{column}: przeciągnij, aby przenieść, lub użyj strzałek w lewo i w prawo",
128
+ gridResizeHelp: "Enter dopasowuje do maksymalnie 200 wczytanych wierszy; strzałki w lewo i w prawo zmieniają szerokość.",
129
+ gridFitColumn: "Dopasuj kolumnę {column}",
130
+ gridFitAllColumns: "Dopasuj wszystkie kolumny",
131
+ gridColumnMoved: "Kolumna {column} przeniesiona na pozycję {position}",
132
+ gridPinColumn: "{column}: przypięcie kolumny",
133
+ gridPinNone: "Przewijana",
134
+ gridPinLeft: "Przypnij z lewej",
135
+ gridPinRight: "Przypnij z prawej",
136
+ gridResponsiveHidden: "Ukryta przy tej szerokości",
137
+ gridFindColumn: "Znajdź kolumnę…",
138
+ gridShowAllColumns: "Pokaż wszystkie",
139
+ gridResetColumns: "Przywróć kolumny",
140
+ gridReorderColumn: "{column}: przeciągnij lub naciśnij Alt+Strzałka w górę albo Alt+Strzałka w dół, aby przenieść",
141
+ gridPinnedLeft: "Przypięta z lewej",
142
+ gridPinnedRight: "Przypięta z prawej",
143
+ gridFilterGroupTitle: "Dodatkowe grupy warunków",
144
+ gridFilterMatch: "Dopasowanie",
145
+ gridFilterAll: "Wszystkie",
146
+ gridFilterAny: "Dowolny",
147
+ gridAddFilterGroup: "Dodaj grupę warunków",
148
+ gridDeleteFilterGroup: "Usuń grupę warunków",
149
+ gridFacetValues: "Wartości i liczby wystąpień po zastosowaniu filtrów",
150
+ gridFacetSearch: "Szukaj wartości",
151
+ gridFacetIncludeCurrent: "Uwzględnij aktywne filtry tej kolumny",
152
+ gridFacetLoad: "Wczytaj wartości",
153
+ gridFacetChoose: "Wybierz wartość",
154
+ gridFacetCounts: "Pasujące wiersze: {count}; wartości: {shown} z {total}.",
155
+ gridFacetReload: "Wartości się zmieniły lub żądanie zostało anulowane. Wczytaj wartości ponownie.",
156
+ gridRenameView: "Zmień nazwę widoku",
157
+ gridDefaultView: "Ustaw jako domyślny",
158
+ gridClearDefaultView: "Usuń ustawienie domyślne",
159
+ gridDefaultViewName: "Widok domyślny: {name}",
160
+ gridNoDefaultView: "Brak widoku domyślnego",
161
+ gridDefaultViewOption: "{name} (domyślny)",
162
+ gridSelection: "Zaznaczenie",
163
+ gridSelectPage: "Zaznacz stronę",
164
+ gridSelectMatching: "Zaznacz wszystkie pasujące",
165
+ gridClearSelection: "Wyczyść zaznaczenie",
166
+ gridSelectionCount: "{count} zaznaczonych",
167
+ gridSelectionQueryCount: "Zaznaczone pasujące rekordy: {count}; wykluczone: {excluded}",
168
+ gridSelectionQueryUnknown: "Zaznaczono wszystkie pasujące rekordy; wykluczone: {excluded}",
169
+ gridSelectRecord: "Zaznacz rekord {id}",
170
+ gridGroupHeading: "{column}: {value} (rekordy: {count})",
171
+ gridGroupMissing: "Brak wartości",
172
+ gridGroupExpand: "Rozwiń grupę",
173
+ gridGroupCollapse: "Zwiń grupę",
174
+ gridSummary: "Podsumowanie wyników",
175
+ gridGroupOrder: "Kolejność grup jest ustalona w raporcie",
176
+ gridExportProgress: "Rekordy: {completed} z {total}",
177
+ gridExportReading: "Rekordy: {completed}",
178
+ gridExportCancelled: "Eksport anulowany",
179
+ gridCancelExport: "Anuluj eksport",
180
+ gridPrint: "Drukuj",
181
+ gridCloseRecord: "Zamknij szczegóły rekordu",
182
+ gridContains: "zawiera",
183
+ gridStartsWith: "zaczyna się od",
184
+ gridIn: "jest jedną z wartości",
185
+ gridIsNull: "brak wartości",
186
+ gridIsNotNull: "wartość istnieje",
187
+ visibleColumns: "Widoczne kolumny",
188
+ resizeColumn: "Zmień szerokość kolumny {column}",
189
+ columnWidth: "{width} pikseli",
190
+ loading: "Ładowanie…",
191
+ loadError: "Nie udało się wczytać: {error} (Enter, aby ponowić)",
192
+ queryFailed: "Nie udało się odświeżyć widoku: {error}",
193
+ actions: "Akcje",
194
+ editValue: "Edytuj wartość kolumny {column}",
195
+ saveEdit: "Zapisz",
196
+ cancelEdit: "Anuluj",
197
+ invalidEdit: "Wprowadź prawidłową wartość kolumny {column}.",
198
+ interactionPending: "Oczekiwanie na aplikację…",
199
+ interactionRejected: "Aplikacja odrzuciła operację.",
200
+ interactionFailed: "Operacja nie powiodła się: {error}",
201
+ markMove: "Przenieś ten węzeł",
202
+ moveHere: "Przenieś tutaj",
203
+ moveBefore: "Przenieś przed",
204
+ moveAfter: "Przenieś za",
205
+ cancelMove: "Anuluj przenoszenie",
206
+ showMore: "Pokaż więcej (wczytano {loaded} z {total})",
207
+ showMoreUnknownTotal: "Pokaż więcej (wczytano {loaded})",
208
+ pageLoaded: "Wczytano {loaded} z {total}",
209
+ itemsLoaded: "Elementy: {count}",
210
+ yes: "Tak",
211
+ no: "Nie",
212
+ utcTime: "{time} UTC",
213
+ localTime: "{time} czasu lokalnego",
214
+ mountError: "Nie można wyświetlić widoku: {error}",
215
+ });
216
+ const staticLabels = Object.freeze({
217
+ overflowNote: "Pokazano {shown} z {total} wierszy.",
218
+ unknownTotal: "nieznanej liczby",
219
+ hiddenRows: "Niewyświetlone wiersze poniżej: {count}",
220
+ fullReport: "Otwórz pełny raport",
221
+ });
222
+ export { labels, staticLabels };
223
+ export default labels;
@@ -0,0 +1,229 @@
1
+ /** Values carried by cells and host-governed edit proposals. Numbers must be finite. */
2
+ export type CellValue = string | number | boolean | null;
3
+ export type TimeZone = "utc" | "local";
4
+ export type CheckMode = "independent" | "hierarchical";
5
+ export type CheckState = boolean | "mixed";
6
+ export type SortDirection = "asc" | "desc";
7
+
8
+ export interface CanopyBadge { text: string; tone?: string; title?: string; }
9
+ /** Ordered visual history metadata; raw cell values still drive queries and exports. */
10
+ export interface CellPeriod { period: string; value?: number | null; state?: string | null; tone?: string; }
11
+ export interface CanopyCell {
12
+ value?: CellValue;
13
+ text?: string;
14
+ /** Alternate flat-grid search text, independent of display, typed queries and export. */
15
+ searchText?: string;
16
+ title?: string;
17
+ tone?: string;
18
+ badges?: CanopyBadge[];
19
+ /** Secondary identifier text, independent of the raw value. */
20
+ secondary?: string;
21
+ /** At most 4096 ordered periods for a flat-grid history presenter. */
22
+ series?: CellPeriod[];
23
+ }
24
+ export type Cell = CellValue | CanopyCell;
25
+
26
+ export interface TableDetail {
27
+ type: "table";
28
+ height?: number;
29
+ caption?: string;
30
+ columns: string[];
31
+ widths?: number[];
32
+ rows: Cell[][];
33
+ }
34
+ export interface PropertiesDetail {
35
+ type: "properties";
36
+ height?: number;
37
+ items: { name: string; value: Cell }[];
38
+ }
39
+ /** HTML details contain content the host explicitly trusts. */
40
+ export interface HtmlDetail { type: "html"; height?: number; html: string; }
41
+ export type CanopyDetail = TableDetail | PropertiesDetail | HtmlDetail;
42
+
43
+ /** A host-owned node. IDs are unique within the view; lazy nodes omit children. */
44
+ interface NodeDefinition {
45
+ id: string;
46
+ label: string;
47
+ kind?: string;
48
+ icon?: string;
49
+ group?: boolean;
50
+ cells?: Record<string, Cell>;
51
+ badges?: CanopyBadge[];
52
+ flags?: string[];
53
+ data?: Record<string, string>;
54
+ expanded?: boolean;
55
+ className?: string;
56
+ }
57
+
58
+ export interface CanopyColumn {
59
+ id: string;
60
+ title: string;
61
+ width: number;
62
+ /** Minimum manual width in CSS pixels; defaults to 40. */
63
+ minWidth?: number;
64
+ /** Maximum manual width in CSS pixels; defaults to 2147483647. */
65
+ maxWidth?: number;
66
+ kind: "text" | "number" | "datetime";
67
+ sortable?: boolean;
68
+ hidden?: boolean;
69
+ editable?: boolean;
70
+ }
71
+ export interface CanopyFilter {
72
+ id: string;
73
+ label: string;
74
+ flag: string;
75
+ ancestorFlag?: string;
76
+ hidden?: boolean;
77
+ }
78
+ export interface ContextAction { id: string; label: string; }
79
+ export interface CheckIntent { readonly path: readonly string[]; readonly checked: boolean; }
80
+ export interface Sort { column: string; direction: SortDirection; }
81
+ export interface ViewQuery { version: 1; sort: Sort | null; filters: string[]; }
82
+ /** Source callbacks observe frozen query snapshots, including their sort and filter values. */
83
+ export interface ReadonlyViewQuery {
84
+ readonly version: 1;
85
+ readonly sort: Readonly<Sort> | null;
86
+ readonly filters: readonly string[];
87
+ }
88
+ export type LeafNode = NodeDefinition & {
89
+ hasChildren?: false; children?: []; childCount?: never; ref?: never; detail?: never;
90
+ };
91
+ export type DetailNode = NodeDefinition & {
92
+ detail: CanopyDetail; hasChildren?: false; children?: never; childCount?: never; ref?: never;
93
+ };
94
+ export interface InlineBranch<TNode = CanopyNode> extends NodeDefinition {
95
+ children: TNode[]; hasChildren?: true; childCount?: number; ref?: never; detail?: never;
96
+ }
97
+ export type LazyNode = NodeDefinition & {
98
+ hasChildren: true; childCount?: number; ref?: string; children?: never; detail?: never;
99
+ };
100
+ /** A node chooses one leaf, detail, complete branch, or lazy branch payload. */
101
+ export type CanopyNode = LeafNode | DetailNode | InlineBranch | LazyNode;
102
+ /** Inline roots and every descendant are fully materialized. */
103
+ export type InlineNode = LeafNode | DetailNode | InlineBranch<InlineNode>;
104
+
105
+ /** Names accepted by the interactive runtime; omitted translations use English. */
106
+ export type LabelName =
107
+ | "gridPaging" | "gridPageRange" | "gridPageNumber" | "gridPageLength" | "gridFirstPage" | "gridPreviousPage" | "gridNextPage" | "gridLastPage"
108
+ | "gridUnknownCount" | "gridMatchedCount" | "gridCursorRange"
109
+ | "gridResultCount" | "gridClearQuery" | "gridRemoveSearch" | "gridGroupedFilters" | "gridRemoveGroup" | "gridRemoveCondition"
110
+ | "gridExport" | "gridFilteredResults" | "gridSelectedRows" | "gridAllResults" | "gridDownloadCsv" | "gridCopy" | "gridDetails"
111
+ | "searchPlaceholder" | "searchResults" | "searchResultCount" | "searchResultCountOfTotal"
112
+ | "searchResultCountOfMany" | "searchNoResults" | "searchTooShort" | "searchWordTooShort" | "searchFailed"
113
+ | "columns" | "visibleColumns" | "resizeColumn" | "columnWidth" | "loading" | "loadError" | "queryFailed"
114
+ | "showMore" | "showMoreUnknownTotal" | "pageLoaded" | "itemsLoaded" | "yes" | "no"
115
+ | "utcTime" | "localTime" | "mountError" | "actions" | "editValue" | "saveEdit" | "cancelEdit"
116
+ | "invalidEdit" | "interactionPending" | "interactionRejected" | "interactionFailed"
117
+ | "markMove" | "moveHere" | "moveBefore" | "moveAfter" | "cancelMove"
118
+ | "gridFilters" | "gridColumn" | "gridOperator" | "gridValue" | "gridValueType" | "gridText" | "gridBoolean" | "gridAddFilter" | "gridRemoveFilter" | "gridApply" | "gridClearFilters" | "gridMoveEarlier" | "gridMoveLater" | "gridViews" | "gridViewName" | "gridSaveView" | "gridRemoveView" | "gridResetView" | "gridChooseView" | "gridInvalidFilter" | "gridContains" | "gridStartsWith" | "gridIn" | "gridIsNull" | "gridIsNotNull"
119
+ | "gridBetween" | "gridRangeEnd" | "gridInvalidRange" | "gridSuggestedCategory" | "gridChooseCategory" | "gridBooleanValue" | "gridTrue" | "gridFalse" | "gridHeaderFilter"
120
+ | "gridExpandCell" | "gridCloseCell" | "gridCloseRecord" | "gridMoveColumn" | "gridResizeHelp" | "gridColumnMoved" | "gridFitColumn" | "gridFitAllColumns"
121
+ | "gridPinColumn" | "gridPinNone" | "gridPinLeft" | "gridPinRight" | "gridResponsiveHidden"
122
+ | "gridFindColumn" | "gridShowAllColumns" | "gridResetColumns" | "gridReorderColumn" | "gridPinnedLeft" | "gridPinnedRight"
123
+ | "gridFilterGroupTitle" | "gridFilterMatch" | "gridFilterAll" | "gridFilterAny" | "gridAddFilterGroup" | "gridDeleteFilterGroup"
124
+ | "gridFacetValues" | "gridFacetSearch" | "gridFacetIncludeCurrent" | "gridFacetLoad" | "gridFacetChoose" | "gridFacetCounts" | "gridFacetReload"
125
+ | "gridRenameView" | "gridDefaultView" | "gridClearDefaultView" | "gridDefaultViewName" | "gridNoDefaultView" | "gridDefaultViewOption"
126
+ | "gridSelection" | "gridSelectPage" | "gridSelectMatching" | "gridClearSelection" | "gridSelectionCount"
127
+ | "gridSelectionQueryCount" | "gridSelectionQueryUnknown" | "gridSelectRecord"
128
+ | "gridGroupHeading" | "gridGroupMissing" | "gridGroupExpand" | "gridGroupCollapse" | "gridSummary" | "gridGroupOrder"
129
+ | "gridExportProgress" | "gridExportReading" | "gridExportCancelled" | "gridCancelExport" | "gridPrint"
130
+ | "gridFitContent"
131
+ | "gridUnpin"
132
+ | "gridWhere"
133
+ | "gridComplexFilter"
134
+ | "gridClearColumnFilter"
135
+ | "gridSelectionAggregate"
136
+ | "gridConditionValue"
137
+ | "gridConditionUpperValue"
138
+ | "gridDeleteCondition"
139
+ | "gridShowRowsWhere"
140
+ | "gridOfTheseAreTrue"
141
+ | "gridOfThese"
142
+ | "gridGroupCount"
143
+ | "gridMore" | "gridDone" | "gridClearAll" | "gridRows" | "gridResultNoun" | "gridSearchNoun" | "gridChart" | "gridColumnMenu" | "gridSortAscending" | "gridSortDescending" | "gridClearSort" | "gridFilterColumn" | "gridHideColumn" | "gridGroupedBy" | "gridRemoveGrouping" | "gridSelectAllValues" | "gridFilterValues" | "gridAddCondition" | "gridAddGroup" | "gridAnd" | "gridOr" | "gridEditAsText" | "gridFilterText" | "gridDeepFilters" | "gridToday" | "gridYesterday" | "gridLastSevenDays" | "gridLastThirtyDays" | "gridThisMonth" | "gridDateRange" | "gridEmpty" | "gridEmptyReason" | "gridNoMatches" | "gridNoMatchesReason" | "gridRetry" | "gridLoadFailed" | "gridAll" | "gridPageOf" | "gridStatusCount" | "gridDetailsSection" | "gridPreviousRecord" | "gridNextRecord" | "gridShowMore" | "gridNoData" | "gridPeriodRange" | "gridHealthy";
144
+ export type CanopyLabels = Partial<Record<LabelName, string>>;
145
+
146
+ export interface InlineSourceDefinition { kind: "inline"; }
147
+ export interface SidecarSourceDefinition { kind: "sidecar"; base: string; }
148
+ /** URL templates use the placeholders defined in docs/data-protocol.md. */
149
+ export interface RemoteSourceDefinition {
150
+ kind: "remote";
151
+ children: string;
152
+ search?: string;
153
+ counts?: string;
154
+ queryAware?: boolean;
155
+ }
156
+ export type SourceDefinition = InlineSourceDefinition | SidecarSourceDefinition | RemoteSourceDefinition;
157
+
158
+ interface ViewDefinitionBase {
159
+ protocol: "canopyx/2";
160
+ type: "view";
161
+ id: string;
162
+ label: string;
163
+ rowHeight: number;
164
+ pageSize: number;
165
+ selection: "single" | "multiple";
166
+ columns: CanopyColumn[];
167
+ icons?: Record<string, string>;
168
+ search?: { minLength: number; maxResults: number; placeholder?: string; kinds?: string[] };
169
+ filters?: CanopyFilter[];
170
+ timeZone?: TimeZone;
171
+ pinTreeColumn?: boolean;
172
+ stateKey?: string;
173
+ deepLink?: boolean;
174
+ labels?: CanopyLabels;
175
+ dragDrop?: boolean;
176
+ contextActions?: ContextAction[];
177
+ }
178
+ /** Checking intents describe a view only when its checkbox mode is enabled. */
179
+ export type ViewDefinition = ViewDefinitionBase & (
180
+ | { checkboxes: CheckMode; checkIntents?: readonly CheckIntent[] }
181
+ | { checkboxes?: never; checkIntents?: never }
182
+ );
183
+ /** Inline views own roots. Sidecar and remote views read roots from their source. */
184
+ export type InlineView = ViewDefinition & { source: InlineSourceDefinition; roots: InlineNode[] };
185
+ export type SidecarView = ViewDefinition & { source: SidecarSourceDefinition; roots?: never };
186
+ export type RemoteView = ViewDefinition & { source: RemoteSourceDefinition; roots?: never };
187
+ export type CanopyView = InlineView | SidecarView | RemoteView;
188
+
189
+ export interface PageResult {
190
+ items: CanopyNode[];
191
+ next?: string | null;
192
+ offset?: number;
193
+ total?: number;
194
+ revision?: string;
195
+ }
196
+ export interface PageDocument extends PageResult {
197
+ protocol: "canopyx/2";
198
+ type: "page";
199
+ parent: string | null;
200
+ next: string | null;
201
+ offset: number;
202
+ }
203
+ export interface SearchHit { path: string[]; label: string; kind?: string; context?: string; }
204
+ export interface SearchResult { hits: SearchHit[]; truncated: boolean; total?: number; revision?: string; }
205
+ export interface SearchDocument extends SearchResult { protocol: "canopyx/2"; type: "search"; query: string; }
206
+ export interface CountUpdate { id: string; childCount?: number; cells?: Record<string, Cell>; flags?: string[]; }
207
+ export interface CountsDocument {
208
+ protocol: "canopyx/2";
209
+ type: "counts";
210
+ items: CountUpdate[];
211
+ revision?: string;
212
+ }
213
+
214
+ /** Versioned presentation state. Paths can name unloaded nodes; restore validates before changing presentation. */
215
+ export interface ViewState {
216
+ version: 1;
217
+ expanded: string[][];
218
+ selection: string[][];
219
+ columns: { id: string; width: number; hidden: boolean }[];
220
+ sort: Sort | null;
221
+ filters: string[];
222
+ timeZone: TimeZone;
223
+ pinTreeColumn: boolean;
224
+ focus: string[] | null;
225
+ anchor: { path: string[]; offset: number } | null;
226
+ checkboxes?: CheckMode;
227
+ checkIntents?: readonly CheckIntent[];
228
+ }
229
+ export interface LegacyViewState { expanded: string[][]; version?: never; }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evotec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,199 @@
1
- # Temporary Holding Version
1
+ # CanopyX - Interactive Trees and Data Grids
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Turn application data into searchable trees, sortable tables and standalone HTML reports. Use C# to write a report you can open in a browser, or mount the JavaScript runtime in your own page.
4
+
5
+ 📦 **.NET packages**
6
+
7
+ [![CanopyX NuGet](https://img.shields.io/nuget/v/CanopyX?label=CanopyX)](https://www.nuget.org/packages/CanopyX)
8
+ [![CanopyX.Html NuGet](https://img.shields.io/nuget/v/CanopyX.Html?label=CanopyX.Html)](https://www.nuget.org/packages/CanopyX.Html)
9
+
10
+ 🌐 **JavaScript package**
11
+
12
+ [![npm version](https://img.shields.io/npm/v/@evotecit/canopyx)](https://www.npmjs.com/package/@evotecit/canopyx)
13
+ [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/license/mit)
14
+
15
+ ## What you can build
16
+
17
+ - **File and folder explorers** with expandable branches, lazy loading, search and multi-selection.
18
+ - **Inventory and results tables** with typed sorting, filters, column resizing and saved views.
19
+ - **HTML reports** that include their own runtime and data and can be opened from disk or a file share.
20
+ - **Application views** backed by paged or cursor-based data sources, with selection and detail callbacks.
21
+ - **Email and print tables** rendered from the same .NET view without JavaScript.
22
+
23
+ ![File explorer in the light theme, with sizes, dates, owners and an expanded detail row](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.1.1/docs/images/tree-grid-light.png)
24
+
25
+ The same explorer in the dark theme, alongside its script-free output for email and print:
26
+
27
+ | Dark theme | Static outline |
28
+ | --- | --- |
29
+ | ![File explorer in the dark theme](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.1.1/docs/images/tree-grid-dark.png) | ![Static outline table](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.1.1/docs/images/static-outline.png) |
30
+
31
+ ## Features
32
+
33
+ | | Tree grid | Data grid |
34
+ | --- | --- | --- |
35
+ | Navigation | Expandable branches, path search and lazy children | Continuous or paged records, offset or cursor navigation |
36
+ | Querying | Search, sibling sorting and named filters | Search, multi-column sorting, typed filters and condition groups |
37
+ | Columns | Resize and hide | Resize, hide, reorder, pin and fit to content |
38
+ | Selection | Single/multiple selection and hierarchical checkboxes | Single/multiple selection with stable record IDs |
39
+ | Presentation | Badges, details, custom cells and light/dark themes | Links, progress, wrapped text, row highlighting and light/dark themes |
40
+ | Delivery | Single HTML file, sidecar bundle or hosted data | Single HTML file or hosted data |
41
+
42
+ Both views use virtual rows and support keyboard navigation. Reporting grids also provide grouping, aggregates, saved views, CSV export and row copying. English labels are built in; Polish, German and Spanish packs are included.
43
+
44
+ ## Installation
45
+
46
+ ### .NET
47
+
48
+ ```shell
49
+ dotnet add package CanopyX.Html
50
+ ```
51
+
52
+ `CanopyX.Html` includes the browser assets and brings in `CanopyX` for the view definitions. Install `CanopyX` alone when you only need the model, tree bundle writer or script-free table renderer.
53
+
54
+ | Target | Support |
55
+ | --- | --- |
56
+ | .NET 8 and .NET 10 | Windows, Linux and macOS |
57
+ | .NET Standard 2.0 | Compatible .NET applications |
58
+ | .NET Framework 4.7.2 | Windows |
59
+
60
+ Neither .NET package adds third-party runtime dependencies. Generating a report from C# needs no Node.js, npm or JavaScript build step.
61
+
62
+ ### JavaScript
63
+
64
+ ```shell
65
+ npm install @evotecit/canopyx
66
+ ```
67
+
68
+ The package includes ES modules, classic scripts, stylesheets and TypeScript declarations, with no runtime package dependencies.
69
+
70
+ ## Usage in .NET
71
+
72
+ ### Create a file explorer
73
+
74
+ ```csharp
75
+ using CanopyX;
76
+ using CanopyX.Html;
77
+
78
+ var files = new CanopyTreeView("files", "Project files");
79
+ files.Search = new CanopySearchOptions();
80
+ files.Columns.Add(new CanopyColumn("name", "Name") { Sortable = true });
81
+ files.Columns.Add(new CanopyColumn("size", "Size")
82
+ {
83
+ Kind = CanopyColumnKind.Number,
84
+ Sortable = true
85
+ });
86
+
87
+ var projects = new CanopyNode("projects", "Projects");
88
+ var report = new CanopyNode("quarterly-report", "Quarterly report.pdf");
89
+ report.Cells["size"] = new CanopyCell(2048L) { Text = "2 KiB" };
90
+ projects.Children.Add(report);
91
+ files.Roots.Add(projects);
92
+
93
+ files.SaveHtmlPage("files.html");
94
+ ```
95
+
96
+ Open `files.html` in a browser and expand Projects. The first column displays the node label; the Size column displays the matching cell. Numeric values control sorting while `Text` controls the displayed value.
97
+
98
+ For a larger tree, write a bundle that loads branches on demand:
99
+
100
+ ```csharp
101
+ files.SaveHtmlBundle("file-report");
102
+ ```
103
+
104
+ Keep the generated page, assets and data files together. The bundle can be opened directly from disk or a file share.
105
+
106
+ ### Create an inventory table
107
+
108
+ ```csharp
109
+ using CanopyX;
110
+ using CanopyX.Html;
111
+
112
+ var inventory = new CanopyDataGridView("inventory", "Inventory");
113
+ inventory.Columns.Add(new CanopyColumn("name", "Name") { Sortable = true });
114
+ inventory.Columns.Add(new CanopyColumn("size", "Size")
115
+ {
116
+ Kind = CanopyColumnKind.Number,
117
+ Sortable = true
118
+ });
119
+
120
+ foreach (var (name, bytes) in new[] { ("Archive", 2048L), ("Documents", 512L) })
121
+ {
122
+ var row = new CanopyGridRow(name);
123
+ row.Cells["name"] = name;
124
+ row.Cells["size"] = new CanopyCell(bytes) { Text = $"{bytes:N0} bytes" };
125
+ inventory.Rows.Add(row);
126
+ }
127
+
128
+ inventory.SaveHtmlPage("inventory.html");
129
+ ```
130
+
131
+ Open `inventory.html` to search the records, sort by name or size, and adjust the columns. Record IDs stay stable when the order changes.
132
+
133
+ ### Render a table for email or print
134
+
135
+ ```csharp
136
+ using CanopyX.Rendering;
137
+
138
+ string treeTable = CanopyStaticHtml.Render(files);
139
+ string inventoryTable = CanopyStaticHtml.RenderGrid(inventory);
140
+ ```
141
+
142
+ These methods return HTML tables with no scripts. Insert the returned HTML into your email, print layout or document-generation workflow.
143
+
144
+ ## Usage in JavaScript
145
+
146
+ Add a container to your page:
147
+
148
+ ```html
149
+ <div id="files" style="height: 360px"></div>
150
+ ```
151
+
152
+ Then mount a tree grid:
153
+
154
+ ```javascript
155
+ import { createTreeGrid } from "@evotecit/canopyx";
156
+ import "@evotecit/canopyx/styles.css";
157
+
158
+ const grid = createTreeGrid(document.getElementById("files"), {
159
+ view: {
160
+ protocol: "canopyx/2", type: "view", id: "files", label: "Project files",
161
+ rowHeight: 28, pageSize: 100, selection: "single",
162
+ search: { minLength: 1, maxResults: 50 },
163
+ columns: [{ id: "name", title: "Name", kind: "text", width: 320 }],
164
+ source: { kind: "inline" },
165
+ roots: [{
166
+ id: "projects", label: "Projects",
167
+ children: [{ id: "quarterly-report", label: "Quarterly report.pdf" }]
168
+ }]
169
+ }
170
+ });
171
+
172
+ await grid.ready;
173
+ ```
174
+
175
+ The stylesheet import uses your application's asset pipeline. For a page without a bundler, serve `classic.js` and `styles.css` from the installed package and load them with `<script>` and `<link>` elements; the script exposes `window.CanopyX`. Use one matching runtime and stylesheet per page.
176
+
177
+ ### Choose a runtime
178
+
179
+ | Import | Use it for | Stylesheet |
180
+ | --- | --- | --- |
181
+ | `@evotecit/canopyx` | Interactive tree grids | `@evotecit/canopyx/styles.css` |
182
+ | `@evotecit/canopyx/grid` | Flat data grids | `@evotecit/canopyx/grid.css` |
183
+ | `@evotecit/canopyx/reporting` | Flat grids with reporting controls | `@evotecit/canopyx/reporting.css` |
184
+
185
+ Module imports are safe on the server; mounting a view requires a browser. The included TypeScript declarations describe the view, data-source and event APIs.
186
+
187
+ ### Translate the controls
188
+
189
+ Import labels from `@evotecit/canopyx/locales/pl`, `/locales/de` or `/locales/es` and pass them as `labels` in your view definition. In C#, set the view's `Labels` to `CanopyLabels.Polish()`, `German()` or `Spanish()` before writing the page.
190
+
191
+ ## Working with larger datasets
192
+
193
+ Virtual rows keep the rendered DOM small. Tree bundles split data into sidecar files, and hosted sources let your application fetch records as the reader navigates. Flat-grid queries filter and sort the complete dataset before paging; loading one page does not mean the rest of the results are available in the browser.
194
+
195
+ CSV export and copying distinguish filtered results, selected records and all records. For Office files, large exports or application actions, connect your own callbacks to the view.
196
+
197
+ ## Community and license
198
+
199
+ CanopyX is an Evotec project, licensed under MIT. Questions and feedback are welcome in the [Evotec community on Discord](https://evo.yt/discord). You can find more Evotec projects and articles at [evotec.xyz](https://evotec.xyz/hub).