@evotecit/canopyx 0.1.3 → 0.2.0

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 (85) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +113 -0
  2. package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
  3. package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
  4. package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
  5. package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
  6. package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
  7. package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
  8. package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
  9. package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
  10. package/CanopyX.Html/Assets/canopyx-data.js +14 -0
  11. package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
  12. package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
  13. package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
  14. package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
  15. package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
  16. package/CanopyX.Html/Assets/canopyx-export.js +18 -0
  17. package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
  18. package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
  19. package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
  20. package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
  21. package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
  22. package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
  23. package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
  24. package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
  25. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
  26. package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
  27. package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
  28. package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
  29. package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
  30. package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
  31. package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
  32. package/CanopyX.Html/Assets/canopyx-history.js +13 -0
  33. package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
  34. package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
  35. package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
  36. package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
  37. package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
  38. package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
  39. package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
  40. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
  41. package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
  42. package/CanopyX.Html/Assets/canopyx-link.css +19 -0
  43. package/CanopyX.Html/Assets/canopyx-link.js +38 -0
  44. package/CanopyX.Html/Assets/canopyx-links.js +14 -0
  45. package/CanopyX.Html/Assets/canopyx-live.css +24 -0
  46. package/CanopyX.Html/Assets/canopyx-live.js +88 -0
  47. package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
  48. package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
  49. package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
  50. package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
  51. package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
  52. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
  53. package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
  54. package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
  55. package/CanopyX.Html/Assets/canopyx-search.js +32 -0
  56. package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
  57. package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
  58. package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
  59. package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
  60. package/CanopyX.Html/Assets/canopyx.css +17 -5
  61. package/CanopyX.Html/Assets/canopyx.js +175 -96
  62. package/JavaScript/calendar.d.ts +362 -0
  63. package/JavaScript/cell-ranges.d.ts +76 -0
  64. package/JavaScript/column-groups.d.ts +32 -0
  65. package/JavaScript/editing.d.ts +257 -0
  66. package/JavaScript/enhance.d.ts +202 -0
  67. package/JavaScript/facets.d.ts +58 -0
  68. package/JavaScript/formatting.d.ts +88 -0
  69. package/JavaScript/grid.d.ts +409 -56
  70. package/JavaScript/hosts.d.ts +177 -2
  71. package/JavaScript/index.d.ts +53 -4
  72. package/JavaScript/live.d.ts +115 -0
  73. package/JavaScript/locales/de.mjs +304 -0
  74. package/JavaScript/locales/es.mjs +304 -0
  75. package/JavaScript/locales/fr.d.ts +6 -0
  76. package/JavaScript/locales/fr.mjs +527 -0
  77. package/JavaScript/locales/it.d.ts +6 -0
  78. package/JavaScript/locales/it.mjs +527 -0
  79. package/JavaScript/locales/pl.mjs +304 -0
  80. package/JavaScript/plugins.d.ts +113 -0
  81. package/JavaScript/protocol.d.ts +139 -6
  82. package/JavaScript/records.d.ts +136 -0
  83. package/README.md +61 -9
  84. package/README.nuget.md +1 -1
  85. package/package.json +99 -3
@@ -1,11 +1,42 @@
1
1
  import CanopyX from "./index.js";
2
2
  import type { TreeGrid } from "./index.js";
3
- import type { CanopyColumn, CanopyLabels, Cell, CellValue, SortDirection, TimeZone } from "./protocol.js";
4
- import type { HostOptions } from "./hosts.js";
3
+ import type { CanopyColumn, CanopyLabels, Cell, CellValue, SortDirection, TimeZone, SearchHighlightOptions, LayoutOptions, LayoutState, ViewModeMethods } from "./protocol.js";
4
+ import type { HostOptions, ExportFormat, ExportFormatOptions, ExportFormatResult, LinkedView } from "./hosts.js";
5
+ import type { CanopyPlugin } from "./plugins.js";
6
+ import type { GridEditorDefinition, GridValidation, GridEditingSettings, GridEditingOptions, GridEditingApi, GridCellValueChange,
7
+ GridRowCommit, GridCommitRowResult, GridCommitOutcome, GridCommit, GridCommitResult } from "./editing.js";
8
+ import type { RecordPanelDefinition, RecordLoader, RecordSectionRenderer, RecordAction, RecordOpenAll, OpenRecordOptions } from "./records.js";
9
+ export * from "./records.js";
5
10
  export * from "./index.js";
11
+ export * from "./enhance.js";
12
+ import type { enhance, grid } from "./enhance.js";
13
+ export * from "./editing.js";
14
+ import type { GridExportFormat, GridFormatOptions, GridFormatRule } from "./formatting.js";
15
+ export * from "./formatting.js";
16
+ import type { GridColumnGroup, GridColumnGroupApi } from "./column-groups.js";
17
+ import type { GridRangeSelection, GridClipboardSettings, GridCellRange, GridPasteEvent, GridCellRangeApi } from "./cell-ranges.js";
18
+ export * from "./column-groups.js";
19
+ export * from "./cell-ranges.js";
20
+ import type { GridLiveDefinition, GridLiveOptions, GridLiveApi, GridLiveResult } from "./live.js";
21
+ import type { GridFacetPanelDefinition, GridFacetPanelOptions, GridFacetPanelApi } from "./facets.js";
22
+ import type { GridCalendarDefinition, GridCalendarOptions, GridCalendarApi, GridCalendarState, GridCalendarSavedState, GridOccurrenceChange, GridRangeSource, GridEventCreate, GridEventReceive, CalendarOptions } from "./calendar.js";
23
+ export * from "./facets.js";
24
+ export * from "./calendar.js";
25
+ export * from "./live.js";
6
26
 
7
27
  /** A stable record identity and typed cells. Flat records have no tree fields. */
8
- export interface GridRow { id: string; cells: Record<string, Cell>; tone?: string; className?: string; }
28
+ export interface GridRow { id: string; cells: Record<string, Cell>; tone?: string; className?: string;
29
+ /** Reporting detail rows: the record's table, properties or trusted HTML payload, or its nested grid rows or tree roots. */
30
+ detail?: GridRowDetail;
31
+ /** Values of the view's record fields (record.fields): shown in the record drawer only, never queried, searched, sorted or exported. */
32
+ fields?: Record<string, Cell>; }
33
+ /** A record's detail payload: a table, property list or trusted HTML, or the inline data of the definition's nested view. */
34
+ export type GridRowDetail =
35
+ | { type: "table"; caption?: string; columns: string[]; widths?: number[]; rows: Cell[][] }
36
+ | { type: "properties"; items: { name: string; value: Cell }[] }
37
+ | { type: "html"; html: string }
38
+ | { type: "grid"; rows: GridRow[] }
39
+ | { type: "tree"; roots: unknown[] };
9
40
  /** First match wins separately for the row and each condition-column cell; raw filter semantics apply. */
10
41
  export type GridHighlightRule = GridFilter & { target?: "row" | "cell" } &
11
42
  ({ tone: string; className?: string } | { tone?: string; className: string });
@@ -15,6 +46,8 @@ export interface GridExportCell {
15
46
  readonly text: string;
16
47
  readonly tone?: string;
17
48
  readonly link?: { readonly target: string; readonly tooltip?: string };
49
+ /** The cell's conditional formatting, present when the grid has rules that format it. CSV reads only value and text. */
50
+ readonly format?: GridExportFormat;
18
51
  /** Unsupported presentation must be rejected by strict adapters or explicitly accepted as text-only. */
19
52
  readonly diagnostics?: readonly string[];
20
53
  }
@@ -22,6 +55,10 @@ export interface GridExportCell {
22
55
  export interface GridExportRow {
23
56
  readonly id: string; readonly cells: Readonly<Record<string, GridExportCell>>;
24
57
  readonly tone?: string; readonly diagnostics?: readonly string[];
58
+ /** The record's row formatting, present when a row rule matched it. */
59
+ readonly format?: GridExportFormat;
60
+ /** Present when the export carries details (request.details): the record's detail as a table, properties or trusted HTML. */
61
+ readonly detail?: Extract<GridRowDetail, { type: "table" | "properties" | "html" }>;
25
62
  }
26
63
  /** Captured column intent. Hosts map semantic tones and CSS pixel widths into their output format. */
27
64
  export interface GridExportColumn {
@@ -30,16 +67,44 @@ export interface GridExportColumn {
30
67
  readonly width?: { readonly unit: "css-px"; readonly preferred: number; readonly minimum: number; readonly maximum?: number };
31
68
  readonly wrap?: boolean; readonly alignment?: "left" | "right";
32
69
  readonly presenter?: GridPresenter;
70
+ /** The column number format that produced the display text. */
71
+ readonly format?: GridNumberFormat;
33
72
  readonly diagnostics?: readonly string[];
34
73
  }
35
- export type GridColumn = Omit<CanopyColumn, "editable" | "width"> & {
36
- editable?: false; wrap?: boolean;
74
+ /**
75
+ * The column ids of a row type: the keys of its cells, so `createDataGrid<Server>(…)` checks column ids, facets and column
76
+ * methods against the row. Untyped rows (the default) take any string.
77
+ */
78
+ export type GridColumnId<Row extends GridRow = GridRow> = string extends keyof Row["cells"] ? string : Extract<keyof Row["cells"], string>;
79
+ /** Fraction digits from 0 to 10 (defaults 0 and 3); a minimum above 3 requires an explicit maximum. Literal affixes have 1 to 16 code units. */
80
+ export interface GridNumberFormat {
81
+ readonly minimumFractionDigits?: number; readonly maximumFractionDigits?: number;
82
+ readonly prefix?: string; readonly suffix?: string;
83
+ }
84
+ export type GridColumn<Row extends GridRow = GridRow> = Omit<CanopyColumn, "id" | "editable" | "width"> & {
85
+ /** The column's id; for a typed row, one of its cells. */
86
+ id: GridColumnId<Row>;
87
+ /** Supports query predicates and filter controls. Default true; does not hide values, supported read-only facets, or presentation rules. */
88
+ filterable?: boolean;
89
+ /** Supports grid exports. Default true. Default export selection omits false; explicit requests reject it. */
90
+ exportable?: boolean;
91
+ /** Readers can edit the column's cells; the editing part loads with the grid (docs/editing.md). */
92
+ editable?: boolean;
93
+ /** The column's typed editor; without one the column's kind chooses text, number or datetime. */
94
+ editor?: GridEditorDefinition;
95
+ /** Checks an edited value must pass before its row commits. */
96
+ validation?: GridValidation;
97
+ wrap?: boolean;
37
98
  /** Declarative presentation; explicit host renderers take precedence. */
38
99
  presenter?: GridPresenter;
100
+ /** Number columns: display format of raw numbers without cell text, also used by summaries and selection totals. */
101
+ format?: GridNumberFormat;
39
102
  /** Include selected numeric values in the optional status bar. */
40
103
  aggregatable?: boolean;
41
104
  /** Record drawer section label. */
42
105
  section?: string;
106
+ /** false keeps the column out of the record drawer and record panel. Default true. */
107
+ drawer?: boolean;
43
108
  /** Preferred width; the column grows to its header and loaded content. Omit to use the content width. Saved user widths take precedence. */
44
109
  width?: number;
45
110
  /** Wire documents retain a numeric width and set this flag for initial content fitting. */
@@ -52,7 +117,7 @@ export type GridColumn = Omit<CanopyColumn, "editable" | "width"> & {
52
117
  /** Share of spare width (0..100), given before the identity column. */
53
118
  flex?: number;
54
119
  autoSize?: "content" | "header" | "none";
55
- /** How the column gives way when narrow. Integers follow DataTables responsivePriority: 0-1 essential, 10000+ optional. */
120
+ /** How the column gives way when narrow. Integers: 0-1 essential, 10000+ optional, larger values hide first. */
56
121
  priority?: GridColumnPriority;
57
122
  /** Requested edge; null unpins. Omitted: the identity column pins left. */
58
123
  pinned?: "left" | "right" | null;
@@ -60,6 +125,8 @@ export type GridColumn = Omit<CanopyColumn, "editable" | "width"> & {
60
125
  rowHeader?: boolean;
61
126
  /** Retain this column during responsive fitting; explicit user hiding still applies. */
62
127
  alwaysVisible?: boolean;
128
+ /** Hover tooltip: "auto" shows a cell's full value while it is cut off; a declared column id shows that column's text. */
129
+ tooltip?: string;
63
130
  };
64
131
  export type GridColumnPriority = "essential" | "normal" | "optional" | number;
65
132
  export interface GridSort { readonly column: string; readonly direction: SortDirection; }
@@ -77,12 +144,14 @@ export type GridPresenter =
77
144
  | { readonly kind: "boolean"; readonly inverted?: boolean }
78
145
  | { readonly kind: "list"; readonly separator: string; readonly limit: number }
79
146
  | { readonly kind: "longText"; readonly mono?: boolean }
80
- | { readonly kind: "enum" | "state"; readonly tones?: Readonly<Record<string,string>>; /** Tones shown as plain muted text. */ readonly quietTones?: readonly string[] }
147
+ | { readonly kind: "enum" | "state"; readonly tones?: Readonly<Record<string,string>>; /** Tones shown as plain text instead of a chip or dot. */ readonly quietTones?: readonly string[]; /** Quiet values muted (default) or in the normal text colour. */ readonly quietStyle?: "muted" | "plain" }
81
148
  | { readonly kind: "change"; readonly deltaColumn: string; readonly higherIsGood?: boolean }
82
149
  | { readonly kind: "statusStrip" | "missing" }
83
150
  | ({ readonly kind: "sparklineBars" | "sparklineLine" } & GridHistoryPresentation)
84
151
  | { readonly kind: "identifier"; readonly secondaryColumn?: string; readonly mono?: boolean }
85
- | { readonly kind: "tags"; readonly separator: string; readonly limit?: 3 };
152
+ | { readonly kind: "tags"; readonly separator: string; readonly limit?: 3 }
153
+ /** A structured value's summary: a count (countText with {count}), chips (limit) or the first item and +N. The record panel shows it whole. */
154
+ | { readonly kind: "structured"; readonly display?: "count" | "chips" | "first"; readonly limit?: number; readonly countText?: string };
86
155
  /** Ascending inclusive upper bounds; unmatched values use cell tones. */
87
156
  export interface GridThreshold { readonly upperBound: number; readonly tone: string; }
88
157
  /** Both shared scale bounds must be supplied together; otherwise each cell scales independently. */
@@ -90,10 +159,33 @@ export interface GridHistoryPresentation {
90
159
  readonly minimum?: number; readonly scaleMaximum?: number; readonly thresholds?: readonly GridThreshold[];
91
160
  readonly higherIsGood?: boolean; readonly mark?: "worst" | "last";
92
161
  }
162
+ /** A page-size choice: rows per page, "all" for every row of inline data, or either with its own menu text. */
163
+ export type GridPageLengthChoice = number | "all" | { readonly value: number | "all"; readonly label: string };
164
+ /** Settings of a paged grid's pager; continuous grids ignore them (docs/paging.md). */
165
+ export interface GridPager {
166
+ /** Page-size choices in menu order (at most 16); absent keeps 10, 25, 50, 100 and, for inline data, All. */
167
+ readonly lengths?: readonly GridPageLengthChoice[];
168
+ /** The rows-per-page menu. Default true. */
169
+ readonly lengthMenu?: boolean;
170
+ /** Numbered page buttons: 0 for none (default), or 5 to 15. */
171
+ readonly numbers?: number;
172
+ /** The page number field. Absent shows it unless numbered pages show. */
173
+ readonly pageInput?: boolean;
174
+ /** The first-page and last-page buttons. Default true. */
175
+ readonly firstLast?: boolean;
176
+ /** The result text: the compact range (default), a sentence with the unfiltered total while filtered, or none. */
177
+ readonly info?: "range" | "sentence" | "none";
178
+ }
93
179
  /** Include the reporting entry point when this optional capability set is present. */
94
180
  export interface GridReporting {
95
181
  /** Fixed first ordering key. Group headings and collapse never change record paging/counts/exports. */
96
182
  readonly grouping?: GridSort;
183
+ /** Initial runtime grouping levels, outermost first (at most four). Not combined with grouping. */
184
+ readonly groupBy?: readonly GridGroupLevel[];
185
+ /** Readers group by sortable columns from column menus, the group-by bar and dragged headers. Not combined with grouping. */
186
+ readonly groupable?: boolean;
187
+ /** Expandable detail rows under records; content loads on first expand. */
188
+ readonly detail?: GridDetailDefinition;
97
189
  /** At most 32 generic aggregates over raw full-query and full-group values. */
98
190
  readonly aggregates?: readonly GridAggregate[];
99
191
  /** Adds row checkboxes and explicit page/all-matching selection controls. Requires an enabled selection mode. */
@@ -149,8 +241,14 @@ export interface GridToolbarControls {
149
241
  export?: boolean;
150
242
  /** More menu: host actions, Record details and Print. */
151
243
  more?: boolean;
244
+ /** Each header's column menu button; switched off, column fitting keeps no room for it. */
245
+ columnMenus?: boolean;
152
246
  /** Result count shown with the query chips. */
153
247
  count?: boolean;
248
+ /** The match navigator and the Highlight matches switch of a grid with searchHighlight. */
249
+ matches?: boolean;
250
+ /** The Layout menu of a grid with layout options. */
251
+ layout?: boolean;
154
252
  }
155
253
  /** Runtime switches add the search box; documents keep it in the view's search property. */
156
254
  export interface GridToolbarOptions extends GridToolbarControls {
@@ -161,7 +259,7 @@ export interface GridToolbarOptions extends GridToolbarControls {
161
259
  export type GridToolbarSlot = "start" | "afterSearch" | "afterViews" | "end";
162
260
  /** A more-menu entry: a handler, or (reporting runtime) host content disclosed inside the menu. */
163
261
  export type GridAction<Q extends AcceptedGridQuery = GridQuery> =
164
- | { label: string; run: (event: { query: Q | null; signal: AbortSignal }) => void | PromiseLike<void>; render?: never }
262
+ | { label: string; run: (event: { query: Q | null; signal: AbortSignal; api: DataGrid<Q> }) => void | PromiseLike<void>; render?: never }
165
263
  /** render fills container on the entry's first open (and again after a render that threw); close() closes the menu and
166
264
  * focuses its button; signal aborts when the grid is destroyed. */
167
265
  | { label: string; render: (container: HTMLElement, context: { close(): void; signal: AbortSignal }) => void; run?: never };
@@ -193,6 +291,8 @@ export interface GridQuery {
193
291
  readonly filters: readonly GridFilter[];
194
292
  /** Combined with search and flat filters using AND. */
195
293
  readonly filterGroup?: GridFilterGroup;
294
+ /** Runtime grouping levels (reporting groupBy/groupable definitions); omitted input uses the declared levels, [] ungroups. */
295
+ readonly groupBy?: readonly GridGroupLevel[];
196
296
  }
197
297
  /** Sequential host query. Tokens require their issuing revision; there is no random-access offset. */
198
298
  export interface GridCursorQuery extends Omit<GridQuery, "type" | "offset"> {
@@ -203,12 +303,12 @@ export type AcceptedGridQuery = GridQuery | GridCursorQuery;
203
303
  export type GridQueryInput<Q extends AcceptedGridQuery = GridQuery> = Partial<Q>;
204
304
  type GridCount<Q extends AcceptedGridQuery> = Q extends GridCursorQuery ? number | null : number;
205
305
 
206
- interface GridDefinition<Q extends AcceptedGridQuery = GridQuery> {
306
+ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery, Row extends GridRow = GridRow> {
207
307
  protocol: "canopyx/2";
208
308
  type: "grid";
209
309
  id: string;
210
310
  label: string;
211
- columns: GridColumn[];
311
+ columns: GridColumn<Row>[];
212
312
  /** Fixed CSS pixel height (20..64), or content measurement. Fixed is the default writer behavior. */
213
313
  rowHeight?: number | "auto";
214
314
  /** Graphite spacing; an explicit rowHeight remains authoritative. */
@@ -223,45 +323,104 @@ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery> {
223
323
  statusBar?: boolean;
224
324
  /** Per-control toolbar switches (the search box is the search property). */
225
325
  toolbar?: GridToolbarControls;
326
+ /** Ids of browser-registered export formats the Export menu offers after CSV, in order; an id the page does not register is skipped. */
327
+ exportFormats?: string[];
226
328
  /** A grid whose rows fit is as tall as its rows (default true). */
227
329
  autoHeight?: boolean;
228
330
  /** What a plain row click does; it never selects (default "none"). */
229
331
  rowAction?: "none" | "details" | "link";
230
332
  /** Draw a row tone as a bar at the row start (default false). */
231
333
  rowAccents?: boolean;
334
+ /** How editable columns edit; the host's editing option overrides these. */
335
+ editing?: GridEditingSettings;
336
+ /** Marks search and column-filter matches in cells and the drawer, with a match count and next/previous (docs/search-highlight.md). */
337
+ searchHighlight?: boolean | SearchHighlightOptions;
338
+ /** The Layout menu: fit, scroll and cards, and the density (docs/layout-modes.md); layout.density starts the grid at that density. */
339
+ layout?: LayoutOptions;
232
340
  /** Wire documents retain numeric rowHeight and enable measurement with this optional flag. */
233
341
  autoRowHeight?: boolean;
234
342
  /** Minimum and estimate for the rowHeight: "auto" API shorthand (20..64, default 28). */
235
343
  estimatedRowHeight?: number;
236
344
  /** At most 64 ordered rules; a tone or className is required on each rule. */
237
345
  highlightRules?: readonly GridHighlightRule[];
346
+ /** At most 64 conditional formatting rules in priority order; the formatting part evaluates them (docs/conditional-formatting.md). */
347
+ formatRules?: readonly GridFormatRule[];
348
+ /** The legend and the rule editor. */
349
+ formatting?: GridFormatOptions;
238
350
  pageSize: number;
239
351
  /** Presentation only; pageSize remains the source fetch size. */
240
352
  presentation?: "continuous" | "paged";
241
353
  pageLength?: number;
354
+ /** Pager settings of a paged view, read by the pager part (canopyx-pager.js); absent keeps the built-in pager. */
355
+ pager?: GridPager;
242
356
  selection: "none" | "single" | "multiple";
243
357
  search: boolean;
244
358
  /** Search-input debounce (0..5000). Absent uses 0 for inline and 300 for host sources. */
245
359
  searchDelayMs?: number;
246
360
  /** Unique declared IDs; absent searches every column, including hidden columns. */
247
- searchColumns?: string[];
361
+ searchColumns?: GridColumnId<Row>[];
248
362
  reporting?: GridReporting;
363
+ /** Header rows above the columns, up to three levels (docs/column-groups.md). */
364
+ columnGroups?: readonly GridColumnGroup[];
365
+ /** Cell ranges by mouse, keyboard and touch, the cell menu and the clipboard keys (docs/cell-ranges.md). */
366
+ rangeSelection?: GridRangeSelection;
367
+ /** How cell ranges copy and paste. */
368
+ clipboard?: GridClipboardSettings;
369
+ /** The record panel: record fields, sections, tabs, a pinned summary, deep links, record details on demand (records.d.ts). */
370
+ record?: RecordPanelDefinition;
371
+ /** Live data: rows that change in place, the stable order, flashing cells, batching and polling (live.d.ts). */
372
+ live?: GridLiveDefinition;
373
+ /** A facet panel beside or above the grid: value lists with counts and ranges with histograms (facets.d.ts). */
374
+ facets?: GridFacetPanelDefinition<Row>;
375
+ /** Shows the records as a calendar: month, week, day, N days, agenda, year and the resource timeline (calendar.d.ts). */
376
+ calendar?: GridCalendarDefinition<Row>;
377
+ /** Rows can be dragged out (or picked up with F2) onto a calendar that accepts drops. */
378
+ dragRows?: boolean;
249
379
  stateKey?: string;
250
380
  timeZone?: TimeZone;
251
381
  labels?: Partial<CanopyLabels>;
382
+ /** Sanitizes trusted host HTML details before insertion; shares the tree host's sanitizer contract. */
383
+ sanitizeHtml?: HostOptions["sanitizeHtml"];
252
384
  query?: Q;
253
385
  }
254
- export interface InlineGridView extends GridDefinition { source: { kind: "inline"; navigation?: "offset" }; rows: GridRow[]; }
255
- export interface HostGridView extends GridDefinition { source: { kind: "host"; navigation?: "offset" }; rows?: never; }
256
- export interface CursorGridView extends GridDefinition<GridCursorQuery> {
386
+ export interface InlineGridView<Row extends GridRow = GridRow> extends GridDefinition<GridQuery, Row> { source: { kind: "inline"; navigation?: "offset" }; rows: Row[]; rowData?: never; }
387
+ /**
388
+ * A compact row: the row id (or `{ id, tone?, className? }`) followed by its cells in the order of `GridRowData.columns`.
389
+ * Trailing empty cells may be left out; `null` is an empty cell.
390
+ */
391
+ export type GridCompactRow = [string | { id: string; tone?: string; className?: string }, ...Cell[]];
392
+ /** Compact row data of an inline grid, in place of `rows` (docs/data-protocol.md, "Row data"). */
393
+ export interface GridRowData {
394
+ /** Compact row encoding version, preserved within canopyx/2 for independently deployed readers. */
395
+ version: 1;
396
+ /** Declared column ids, unique, in the order of each compact row's cells. */
397
+ columns: string[];
398
+ /** The number of rows in all chunks. */
399
+ count: number;
400
+ /** The rows are in ordinal identity order, so the first rows show before the last chunk is decoded. */
401
+ ordered?: boolean;
402
+ /** Each string chunk is base64 text of gzip-compressed JSON, inflated with DecompressionStream. */
403
+ encoding?: "gzip";
404
+ /** Folder of the sidecar files `rows/<n>.js`, relative to the page; requires a chunk count. */
405
+ base?: string;
406
+ /**
407
+ * A count of `application/x-canopyx-rows` scripts inside the host element (or of sidecar files under `base`), or the chunks
408
+ * themselves: arrays of compact rows, or strings of chunk JSON (base64 gzip with `encoding`).
409
+ */
410
+ chunks: number | Array<GridCompactRow[] | string>;
411
+ }
412
+ /** An inline grid whose rows the data part decodes from compact row data, chunk by chunk, after the first paint. */
413
+ export interface CompactGridView<Row extends GridRow = GridRow> extends GridDefinition<GridQuery, Row> { source: { kind: "inline"; navigation?: "offset" }; rowData: GridRowData; rows?: never; }
414
+ export interface HostGridView<Row extends GridRow = GridRow> extends GridDefinition<GridQuery, Row> { source: { kind: "host"; navigation?: "offset" }; rows?: never; }
415
+ export interface CursorGridView<Row extends GridRow = GridRow> extends GridDefinition<GridCursorQuery, Row> {
257
416
  source: { kind: "host"; navigation: "cursor" };
258
417
  presentation: "paged";
259
418
  rows?: never;
260
419
  }
261
- export type GridView = InlineGridView | HostGridView | CursorGridView;
420
+ export type GridView<Row extends GridRow = GridRow> = InlineGridView<Row> | CompactGridView<Row> | HostGridView<Row> | CursorGridView<Row>;
262
421
 
263
422
  /** A complete requested window from one dataset revision, with full and matching counts. */
264
- export interface GridPage {
423
+ export interface GridPage<Row extends GridRow = GridRow> {
265
424
  protocol: "canopyx/2";
266
425
  type: "gridPage";
267
426
  view: string;
@@ -269,7 +428,7 @@ export interface GridPage {
269
428
  offset: number;
270
429
  total: number;
271
430
  matched: number;
272
- items: GridRow[];
431
+ items: Row[];
273
432
  }
274
433
  /** Hosts own data access, authorization and query execution; cancellation does not grant write authority. */
275
434
  export interface GridFacetOptions { readonly search?: string; readonly limit?: number; readonly includeCurrentFilter?: boolean; readonly signal?: AbortSignal; }
@@ -287,21 +446,73 @@ export interface GridFacetPage {
287
446
  export interface GridFacetSource<Q extends AcceptedGridQuery = AcceptedGridQuery> {
288
447
  facet?(request: GridFacetQuery<Q>, options: { signal: AbortSignal }): GridFacetPage | PromiseLike<GridFacetPage>;
289
448
  }
290
- export interface GridSource extends GridFacetSource<GridQuery> {
449
+ export interface GridSource<Row extends GridRow = GridRow> extends GridFacetSource<GridQuery>, GridRangeSource<Row> {
291
450
  readonly navigation?: "offset";
292
- query(query: GridQuery, options: { signal: AbortSignal }): GridPage | PromiseLike<GridPage>;
451
+ query(query: GridQuery, options: { signal: AbortSignal }): GridPage<Row> | PromiseLike<GridPage<Row>>;
293
452
  /** Complete-query metadata, echoing the normalized accepted request, revision and matched count. */
294
453
  summary?(query: GridQuery & { readonly revision: string }, options: { signal: AbortSignal }): GridSummary | PromiseLike<GridSummary>;
295
- }
454
+ /** Saves changed rows (a remote source posts the gridCommit document to its commit endpoint) and answers per row. */
455
+ commit?(batch: GridCommit, options: { signal: AbortSignal }): GridCommitResult | PromiseLike<GridCommitResult>;
456
+ /** A record's details when its drawer opens (a gridRecordQuery answered with a gridRecord), as a remote protocol client sends it. */
457
+ record?: RecordLoader;
458
+ /** A record's position in the query's result, or -1, so openRecord and deep links scroll the grid to it. */
459
+ position?(query: GridQuery, id: string, options: { signal: AbortSignal }): number | PromiseLike<number>;
460
+ }
461
+ /** Built-in operations, or the name of a custom aggregate a plugin registers (inline data) or the host source computes. */
462
+ export type GridAggregateOperation = "count" | "sum" | "average" | "minimum" | "maximum" | "distinctCount" | (string & {});
296
463
  export interface GridAggregate {
464
+ /** The label is shown beside the value in group headings and the query totals row. */
297
465
  readonly id: string; readonly column: string; readonly label: string;
298
- readonly operation: "count" | "sum" | "average" | "minimum" | "maximum";
466
+ readonly operation: GridAggregateOperation;
467
+ /** Explicit value format; otherwise the column format applies, except that count and distinctCount use whole numbers. */
468
+ readonly format?: GridNumberFormat;
469
+ }
470
+ /** One runtime grouping level: groups order by value, or by a declared aggregate (nulls last, ties by value). */
471
+ export interface GridGroupLevel {
472
+ readonly column: string; readonly direction: "asc" | "desc"; readonly aggregate?: string;
473
+ }
474
+ /** Declared detail rows: one content source (a page template, a plugin detail renderer or a nested view), else record payloads. */
475
+ export interface GridDetailDefinition {
476
+ /** Id of a page &lt;template&gt;; elements with data-cx-field receive the record's cell text. */
477
+ readonly template?: string;
478
+ /** Name of a detail renderer a plugin registers in detailRenderers. */
479
+ readonly renderer?: string;
480
+ /** Nested grid or tree document whose inline data is each record's grid or tree payload. */
481
+ readonly view?: object;
482
+ /** Exports and print snapshots carry detail payloads; default true. */
483
+ readonly print?: boolean;
484
+ readonly label?: string;
485
+ }
486
+ /** The context of a detail callback. The signal aborts when the record collapses, leaves the result or the grid is destroyed. */
487
+ export interface GridDetailContext {
488
+ readonly id: string; readonly signal: AbortSignal; readonly api: unknown; readonly labels: Readonly<Record<string, string>>;
489
+ onDispose(cleanup: () => void): void;
490
+ }
491
+ /** A nested view a detail callback returns: a grid or tree document, an optional host source and options for createDataGrid/createTreeGrid. */
492
+ export interface GridNestedDetail {
493
+ readonly view: object; readonly source?: unknown; readonly options?: Readonly<Record<string, unknown>>;
494
+ }
495
+ /** Host detail rows (the detail option): one callback draws the content; hasDetail limits which records offer it. */
496
+ export interface GridDetailOptions<Row extends GridRow = GridRow> {
497
+ /** A host template: draws into element (the body inside the visible width). */
498
+ render?(row: Readonly<Row>, element: HTMLElement, context: GridDetailContext): void | PromiseLike<void>;
499
+ /** Trusted markup inserted as HTML: encode the record text you put into it. */
500
+ html?(row: Readonly<Row>, context: GridDetailContext): string | PromiseLike<string>;
501
+ grid?(row: Readonly<Row>, context: GridDetailContext): GridNestedDetail | PromiseLike<GridNestedDetail>;
502
+ tree?(row: Readonly<Row>, context: GridDetailContext): GridNestedDetail | PromiseLike<GridNestedDetail>;
503
+ /** A host source for the definition's nested view. */
504
+ source?(row: Readonly<Row>, context: GridDetailContext): unknown;
505
+ hasDetail?(row: Readonly<Row>): boolean;
506
+ /** The detail exports and print snapshots carry for a record. */
507
+ export?(row: Readonly<Row>): Extract<GridRowDetail, { type: "table" | "properties" | "html" }> | null;
299
508
  }
300
509
  export interface GridGroup {
301
510
  readonly key: string; readonly value: CellValue; readonly offset: number; readonly count: number;
302
511
  readonly aggregates: Readonly<Record<string, number | null>>;
303
512
  /** Host-owned complete-group display values; rendered with column presenters, excluded from record queries and exports. */
304
513
  readonly cells?: Readonly<Record<string, Cell>>;
514
+ /** The next level's groups inside this one: present on every group of an outer level. Keys are unique across levels. */
515
+ readonly groups?: readonly GridGroup[];
305
516
  }
306
517
  export interface GridSummary {
307
518
  readonly protocol: "canopyx/2"; readonly type: "gridSummary"; readonly view: string; readonly revision: string;
@@ -309,16 +520,16 @@ export interface GridSummary {
309
520
  readonly groups: readonly GridGroup[]; readonly aggregates: Readonly<Record<string, number | null>>;
310
521
  }
311
522
  /** One complete visible page. Null counts remain unknown; ordinal positions do not grant random access. */
312
- export interface GridCursorPage extends Omit<GridPage, "type" | "total" | "matched"> {
523
+ export interface GridCursorPage<Row extends GridRow = GridRow> extends Omit<GridPage<Row>, "type" | "total" | "matched"> {
313
524
  type: "gridCursorPage";
314
525
  total: number | null;
315
526
  matched: number | null;
316
527
  nextCursor?: string;
317
528
  previousCursor?: string;
318
529
  }
319
- export interface GridCursorSource extends GridFacetSource<GridCursorQuery> {
530
+ export interface GridCursorSource<Row extends GridRow = GridRow> extends GridFacetSource<GridCursorQuery> {
320
531
  readonly navigation: "cursor";
321
- query(query: GridCursorQuery, options: { signal: AbortSignal }): GridCursorPage | PromiseLike<GridCursorPage>;
532
+ query(query: GridCursorQuery, options: { signal: AbortSignal }): GridCursorPage<Row> | PromiseLike<GridCursorPage<Row>>;
322
533
  }
323
534
  /** Accepted presentation; indices are zero based, while the visible range is one based (0–0 when empty). */
324
535
  export interface GridPaging<Q extends AcceptedGridQuery = GridQuery> {
@@ -366,6 +577,11 @@ export interface GridExportRequest<Q extends AcceptedGridQuery = GridQuery> {
366
577
  /** Reporting delivery captures the canonical locale and a safe suggested basename without a format extension. */
367
578
  readonly locale?: string;
368
579
  readonly fileName?: string;
580
+ /** Filtered exports of a grouped result: levels, totals and nested groups with record ranges for outline levels. */
581
+ readonly grouping?: { readonly levels: readonly (GridGroupLevel & { readonly title: string })[]; readonly aggregates: readonly GridAggregate[];
582
+ readonly totals: Readonly<Record<string, number | null>>; readonly groups: readonly (GridGroup & { readonly text: string })[] };
583
+ /** Records carry their detail payloads. */
584
+ readonly details?: true;
369
585
  }
370
586
  /** Record counts refer to the complete captured export scope, including selected-query exclusions. */
371
587
  export interface GridExportProgress {
@@ -373,9 +589,27 @@ export interface GridExportProgress {
373
589
  readonly completed: number;
374
590
  readonly total: number | null;
375
591
  }
592
+ /** The detail of canopyx:link and the argument of onLink: a link presenter cell the user follows (by click, Enter or the row's link action). */
593
+ export interface GridLinkActivation<Row extends GridRow = GridRow> {
594
+ readonly id: string;
595
+ /** A frozen copy of the loaded row. */
596
+ readonly row: Readonly<Row>;
597
+ /** The id of the column whose link it is. */
598
+ readonly column: GridColumnId<Row>;
599
+ /** The resolved address. */
600
+ readonly href: string;
601
+ }
602
+ /** The detail of canopyx:rowActivate and the argument of onRowActivate on a flat grid. */
603
+ export interface GridRowActivation<Row extends GridRow = GridRow> {
604
+ readonly id: string;
605
+ /** A frozen copy of the loaded row. */
606
+ readonly row: Readonly<Row>;
607
+ /** True for Enter or Ctrl/Command+Enter, false for a click or double click. */
608
+ readonly keyboard: boolean;
609
+ }
376
610
  export interface GridExportOptions {
377
611
  scope: GridExportRequest["scope"];
378
- /** Defaults to visible columns in their current order. */
612
+ /** Defaults to visible exportable columns in their current order. Explicit non-exportable columns are rejected. */
379
613
  columns?: string[];
380
614
  values?: GridExportRequest["values"];
381
615
  signal?: AbortSignal;
@@ -420,6 +654,16 @@ export interface GridState<Q extends AcceptedGridQuery = GridQuery> {
420
654
  collapsedGroups?: string[];
421
655
  /** Optional for compatibility with existing continuous snapshots; restored offsets clamp to current results. */
422
656
  paging?: { pageLength: number; offset: number; allRows?: boolean };
657
+ /** Formatting rules edited at run time, when they differ from the view's; restored with the view. */
658
+ formatRules?: GridFormatRule[];
659
+ /** The reader's layout, in a grid with layout options. */
660
+ layout?: LayoutState;
661
+ /** The reader's Highlight matches switch, in a grid with searchHighlight. */
662
+ highlight?: boolean;
663
+ /** Whether the reader left the facet panel open, in a grid with facets. */
664
+ facets?: { open: boolean };
665
+ /** The calendar's view, date, zoom and collapsed resources, in a grid with a calendar. */
666
+ calendar?: GridCalendarSavedState;
423
667
  }
424
668
  /** Versioned, bounded preference envelope. Revisions/cursors belong to data, never durable settings. */
425
669
  export type GridPreferenceQuery<Q extends AcceptedGridQuery = GridQuery> = Readonly<Omit<Q, "revision" | "cursor">> & {
@@ -444,53 +688,125 @@ export interface GridViewStore<Q extends AcceptedGridQuery = GridQuery> {
444
688
  load(event: { key: string; view: string; signal: AbortSignal }): GridViewSettings<Q> | null | PromiseLike<GridViewSettings<Q> | null>;
445
689
  save(event: { key: string; view: string; settings: GridViewSettings<Q>; signal: AbortSignal }): void | PromiseLike<void>;
446
690
  }
447
- interface GridHostBehavior<Q extends AcceptedGridQuery = GridQuery> {
691
+ interface GridHostBehavior<Q extends AcceptedGridQuery = GridQuery, Row extends GridRow = GridRow> {
448
692
  /** False hides the whole toolbar so a host can compose controls around the same grid controller; an object switches
449
693
  * single controls off (overriding the view's toolbar switches). Switched-off controls are not built. */
450
694
  toolbar?: boolean | GridToolbarOptions;
451
695
  /** Overrides the view's search-input debounce (0..5000); Enter always applies immediately. */
452
696
  searchDelayMs?: number;
453
- source?: Q extends GridCursorQuery ? GridCursorSource : GridSource;
697
+ source?: Q extends GridCursorQuery ? GridCursorSource<Row> : GridSource<Row>;
454
698
  labels?: Partial<CanopyLabels>;
455
- renderers?: Record<string, (row: GridRow, element: HTMLElement, context: GridCellContext) => boolean | void>;
699
+ renderers?: Record<string, (row: Row, element: HTMLElement, context: GridCellContext) => boolean | void>;
456
700
  cachePageLimit?: number;
457
701
  requestTimeoutMs?: number;
702
+ /** Formats the Export menu lists after CSV: ids of registered formats or provider objects, in order. Wins over the view's exportFormats; absent offers every registered format. */
703
+ exportFormats?: readonly (string | ExportFormat<GridExport<Q>>)[];
704
+ /** Safety timeout for export format writers and onExport, 1..2147483647 ms (default 600000). Page reads keep requestTimeoutMs. */
705
+ exportTimeoutMs?: number;
458
706
  onQuery?: (event: GridQueryEvent<Q>) => void;
459
707
  onSelect?: (event: { ids: string[]; selection?: GridSelection<Q> }) => void;
460
708
  onError?: (event: { error: Error }) => void;
461
- onColumnResize?: (event: { column: string; width: number }) => void;
462
- onColumnPin?: (event: { column: string; pin: GridColumnPin }) => void;
709
+ onColumnResize?: (event: { column: GridColumnId<Row>; width: number }) => void;
710
+ onColumnPin?: (event: { column: GridColumnId<Row>; pin: GridColumnPin }) => void;
463
711
  onState?: (event: { query: Q | null; state: GridState<Q> }) => void;
464
- /** Hosts govern Office generation, authorization and delivery. CanopyX performs no domain write. */
712
+ /** The grid's formatting rules changed (setFormatRules, the rule editor or a restored view); canopyx:format carries the same detail. */
713
+ onFormat?: (event: { rules: readonly GridFormatRule[] }) => void;
714
+ /** Hosts govern Office generation, authorization and delivery. CanopyX performs no domain write. Runs under exportTimeoutMs. */
465
715
  onExport?: (event: { format: string; request: GridExportRequest<Q>; capture: GridExport<Q>; signal: AbortSignal;
466
716
  /** Reporting runtime only. Monotonic record counts and phases; late calls after cancellation are ignored. */
467
717
  reportProgress?: (phase: GridExportProgress["phase"], completed: number) => void }) => void | PromiseLike<void>;
468
718
  /** Reporting runtime only. Render the complete snapshot through a host-owned renderer; honor cancellation before delivery. */
469
- onPrint?: (event: { snapshot: GridExportSnapshot<Q>; signal: AbortSignal }) => void | PromiseLike<void>;
719
+ /** After a layout or density switch; the canopyx:layout event carries the same detail. */
720
+ onLayout?: (detail: LayoutState) => void | boolean;
721
+ /** After each finished match count and each move between matches; the canopyx:matches event carries the same detail. */
722
+ onMatches?: (detail: { total: number; current: number; complete: boolean }) => void | boolean;
723
+ /** layout asks for stacked records from a grid in the card layout and a table otherwise; print never scrolls. */
724
+ onPrint?: (event: { snapshot: GridExportSnapshot<Q>; signal: AbortSignal; layout: "table" | "stacked" }) => void | PromiseLike<void>;
470
725
  /** Hosts compose a drawer or details view and restore returnFocus when it closes. */
471
- onDetails?: (event: { row: Readonly<GridRow>; query: Q; signal: AbortSignal; returnFocus: Element | null }) => void | PromiseLike<void>;
726
+ onDetails?: (event: { row: Readonly<Row>; query: Q; signal: AbortSignal; returnFocus: Element | null }) => void | PromiseLike<void>;
727
+ /** Loads a record's details when its record panel opens; used whatever the view's record source declares. */
728
+ loadRecord?: RecordLoader;
729
+ /** Record panel section renderers by name, beside the plugins' drawerSections. */
730
+ recordSections?: Readonly<Record<string, RecordSectionRenderer>>;
731
+ /** Actions of the record actions menu, after the built-in ones. */
732
+ recordActions?: readonly RecordAction[];
733
+ /** Open all in grid of a collection; return false (or cancel canopyx:openAll) to filter the target yourself. */
734
+ onOpenAll?: (event: RecordOpenAll) => boolean | void;
735
+ /** Print one record; return false (or cancel canopyx:recordPrint) to print the sheet yourself. */
736
+ onRecordPrint?: (event: { readonly id: string; readonly element: HTMLElement; readonly dispose: () => void }) => boolean | void;
737
+ /** Row activation: a double click, Enter on a focused row, Ctrl/Command+Enter in a cell, or a click with rowAction (or on a
738
+ * phone). Runs before the row's own action (onDetails or the drawer, or the row's link); returning false keeps that from
739
+ * running, so the host can open its own view of the row. The cancelable canopyx:rowActivate event carries the same detail. */
740
+ onRowActivate?: (event: GridRowActivation<Row>) => boolean | void;
741
+ /** A link cell being followed. Returning false (or cancelling the canopyx:link event) keeps the browser from following it,
742
+ * so the host can route the address itself. */
743
+ onLink?: (event: GridLinkActivation<Row>) => boolean | void;
472
744
  /** Adds Chart to the selection bar. The host owns chart generation. */
473
745
  onChart?: (event: { query: Q | null; selection: GridSelection<Q>; signal: AbortSignal }) => void | PromiseLike<void>;
474
746
  /** Host-owned secondary actions in the more menu. Content entries (render) need the reporting runtime: render fills the
475
747
  * container on first open, focus moves to its first control, Escape folds it back, and close() closes the menu. */
476
748
  actions?: readonly GridAction<Q>[];
749
+ /** Plugins for this grid only, after the page's (CanopyX.use); one replaces a page plugin of the same name. */
750
+ plugins?: readonly CanopyPlugin[];
751
+ /** Loads the link part when the grid is created and reports its selection and own conditions as canopyx:linkchange (LinkChange). */
752
+ linkEvents?: boolean;
753
+ /** Reporting detail rows drawn by the host (a template, trusted HTML or a nested grid or tree). */
754
+ detail?: GridDetailOptions<Row>;
755
+ /** Editing of editable columns: read-only rules, checks, lookups, editors and the batch writer (docs/editing.md). */
756
+ editing?: GridEditingOptions<Q>;
757
+ /** Before a value changes; returning false (or cancelling canopyx:cellValueChanging) keeps the old value. */
758
+ onCellValueChanging?: (event: GridCellValueChange) => boolean | void;
759
+ /** After a value changed. */
760
+ onCellValueChanged?: (event: GridCellValueChange) => void;
761
+ /** A row whose edits are complete and valid. Return a result (or true, or a promise) to save it, false to keep editing, nothing to leave it in the batch. */
762
+ onRowCommit?: (event: GridRowCommit) => GridCommitRowResult | boolean | void | PromiseLike<GridCommitRowResult | true>;
763
+ /** After commitChanges, with its result. */
764
+ onCommit?: (event: GridCommitOutcome) => void;
477
765
  /** Optional host-governed removal of the report's fixed grouping. */
478
766
  onUngroup?: (event: { query: Q | null; signal: AbortSignal }) => void | PromiseLike<void>;
767
+ /** Overrides the view's rangeSelection. */
768
+ rangeSelection?: GridRangeSelection;
769
+ /** Overrides the view's clipboard settings, switch by switch. */
770
+ clipboard?: GridClipboardSettings;
771
+ /** Live data settings, over the view's live object; true turns live data on with the defaults, false keeps the view's
772
+ * settings from applying while the live methods still work (live.d.ts). */
773
+ live?: boolean | GridLiveOptions<Row, Q>;
774
+ /** After rows changed in place (a transaction, a poll, a refresh); canopyx:rowsChanged carries the same detail. */
775
+ onRowsChanged?: (event: GridLiveResult) => void;
776
+ /** Facet panel settings over the view's facets, and an element the panel renders into (facets.d.ts). */
777
+ facets?: GridFacetPanelOptions<Row>;
778
+ /** After the reader opened or collapsed the facet panel; canopyx:facetPanel carries the same detail. */
779
+ onFacetPanel?: (event: { open: boolean }) => void;
780
+ /** Calendar options: the template hook that draws event content (calendar.d.ts). */
781
+ calendar?: GridCalendarOptions;
782
+ /** After the calendar painted a view or range; canopyx:calendar carries the same detail. */
783
+ onCalendar?: (event: GridCalendarState) => void;
784
+ /** Before one occurrence of a series moves; returning false (or cancelling canopyx:occurrenceChange) keeps the series. */
785
+ onOccurrenceChange?: (event: GridOccurrenceChange) => boolean | void;
786
+ /** Before the calendar proposes a new event; returning false (or cancelling canopyx:eventCreate) lets the host open its own form. */
787
+ onCreate?: (event: GridEventCreate) => boolean | void;
788
+ /** Before a dropped item becomes a record; returning false (or cancelling canopyx:eventReceive) lets the host schedule it. */
789
+ onReceive?: (event: GridEventReceive) => boolean | void;
790
+ /** Cells pasted or filled into the grid, before anything changes; returning false (or cancelling canopyx:paste) refuses them all. */
791
+ onPaste?: (event: GridPasteEvent) => boolean | void;
792
+ /** After the cell ranges changed. */
793
+ onCellRangeChange?: (event: { ranges: readonly GridCellRange[] }) => void;
479
794
  }
480
795
  /** A host store binds to an explicit keyed reporting definition; ordinary grids have no store option. */
481
- export type GridHostOptions<Q extends AcceptedGridQuery = GridQuery> = GridHostBehavior<Q> & (
796
+ export type GridHostOptions<Q extends AcceptedGridQuery = GridQuery, Row extends GridRow = GridRow> = GridHostBehavior<Q, Row> & (
482
797
  { viewStore?: never } | {
483
798
  viewStore: GridViewStore<Q>;
484
- view: (Q extends GridCursorQuery ? CursorGridView : InlineGridView | HostGridView) & { stateKey: string; reporting: GridReporting };
799
+ view: (Q extends GridCursorQuery ? CursorGridView<Row> : InlineGridView<Row> | CompactGridView<Row> | HostGridView<Row>) & { stateKey: string; reporting: GridReporting };
485
800
  }
486
801
  );
487
- export type GridOptions = GridHostOptions & ({ view: InlineGridView } | { view: HostGridView; source: GridSource });
488
- export type CursorGridOptions = GridHostOptions<GridCursorQuery> & { view: CursorGridView; source: GridCursorSource };
802
+ /** A flat grid's options; Row types the rows, columns, facets, events and live API (createDataGrid<Row>). */
803
+ export type GridOptions<Row extends GridRow = GridRow> = GridHostOptions<GridQuery, Row> & ({ view: InlineGridView<Row> | CompactGridView<Row> } | { view: HostGridView<Row>; source: GridSource<Row> });
804
+ export type CursorGridOptions<Row extends GridRow = GridRow> = GridHostOptions<GridCursorQuery, Row> & { view: CursorGridView<Row>; source: GridCursorSource<Row> };
489
805
 
490
- /** Presentation controller. Selection uses stable IDs independently of cached or visible rows. */
491
- export interface DataGrid<Q extends AcceptedGridQuery = GridQuery> {
806
+ /** Presentation controller. Selection uses stable IDs independently of cached or visible rows. Row (default GridRow) types its view, column methods and live API. */
807
+ export interface DataGrid<Q extends AcceptedGridQuery = GridQuery, Row extends GridRow = GridRow> extends GridEditingApi, LinkedView, ViewModeMethods, GridColumnGroupApi, GridCellRangeApi, GridLiveApi<Row, Q>, GridFacetPanelApi, GridCalendarApi {
492
808
  readonly element: HTMLElement;
493
- readonly view: GridView;
809
+ readonly view: GridView<Row>;
494
810
  readonly ready: Promise<Q>;
495
811
  getQuery(): Q | null;
496
812
  setQuery(query: GridQueryInput<Q>): Promise<Q>;
@@ -502,15 +818,15 @@ export interface DataGrid<Q extends AcceptedGridQuery = GridQuery> {
502
818
  previousPage(): Promise<Q>;
503
819
  nextPage(): Promise<Q>;
504
820
  setPageLength(length: number | "all"): Promise<Q>;
505
- setColumnWidth(id: string, width: number): void;
821
+ setColumnWidth(id: GridColumnId<Row>, width: number): void;
506
822
  /** Samples at most 200 cached accepted-query records and 512 display code units per value, capped at 420 CSS pixels or maxWidth, and sets a sticky user width. No fetching or viewport scraping. */
507
- autoFitColumn(id: string): Readonly<{ width: number; sampled: number; scope: "loaded" }>;
823
+ autoFitColumn(id: GridColumnId<Row>): Readonly<{ width: number; sampled: number; scope: "loaded" }>;
508
824
  /** Fits every declared column from the same bounded loaded sample, including hidden columns. */
509
825
  autoFitColumns(): Readonly<Record<string, Readonly<{ width: number; sampled: number; scope: "loaded" }>>>;
510
- setColumnPinned(id: string, pin: GridColumnPin): void;
826
+ setColumnPinned(id: GridColumnId<Row>, pin: GridColumnPin): void;
511
827
  /** Drops user widths so the columns return to automatic content sizing. */
512
- autoSizeColumns(ids?: readonly string[]): void;
513
- setColumnPriority(id: string, priority: GridColumnPriority): void;
828
+ autoSizeColumns(ids?: readonly GridColumnId<Row>[]): void;
829
+ setColumnPriority(id: GridColumnId<Row>, priority: GridColumnPriority): void;
514
830
  /** Restores the declared order, visibility, widths and pins; keeps the query. */
515
831
  resetColumns(): void;
516
832
  /** Reporting runtime only: effective layout for every declared column, with the reason a column is out of view. */
@@ -519,9 +835,9 @@ export interface DataGrid<Q extends AcceptedGridQuery = GridQuery> {
519
835
  * data-cx-slot move into their slot when the grid mounts and return to the host element on destroy. */
520
836
  toolbarSlot?(name: GridToolbarSlot): HTMLElement;
521
837
  /** Reporting runtime only: revisioned, bounded full-query aggregation. Never reads the viewport cache for counts. */
522
- getFacets?(column: string, options?: GridFacetOptions): Promise<GridFacetPage>;
523
- setColumnVisible(id: string, visible: boolean): void;
524
- setColumnOrder(ids: string[]): void;
838
+ getFacets?(column: GridColumnId<Row>, options?: GridFacetOptions): Promise<GridFacetPage>;
839
+ setColumnVisible(id: GridColumnId<Row>, visible: boolean): void;
840
+ setColumnOrder(ids: GridColumnId<Row>[]): void;
525
841
  getState(): GridState<Q>;
526
842
  setState(state: GridState<Q> | GridPreferenceState<Q>): Promise<Q>;
527
843
  resetView(): Promise<Q>;
@@ -542,9 +858,21 @@ export interface DataGrid<Q extends AcceptedGridQuery = GridQuery> {
542
858
  getSelectionDescriptor?(): GridSelection<Q>;
543
859
  /** Reporting summary capability only; null before acceptance. */
544
860
  getSummary?(): GridSummary | null;
545
- /** Flat grouping only. Collapse affects display; record query/selection/export scope remain intact. */
861
+ /** Grouped reporting grids (fixed or runtime). Keys name groups of any level. Collapse affects display; record query/selection/export scope remain intact. */
546
862
  setGroupCollapsed?(key: string, collapsed: boolean): void;
547
863
  getCollapsedGroups?(): string[];
864
+ /** Runtime grouping (groupBy or groupable): sets the levels, outermost first; [] ungroups. Resolves with the accepted query. */
865
+ setGroupBy?(groupBy: readonly GridGroupLevel[]): Promise<Q>;
866
+ getGroupBy?(): readonly GridGroupLevel[];
867
+ expandAllGroups?(): Promise<void>;
868
+ collapseAllGroups?(): Promise<void>;
869
+ /** Level 1 is the outermost; 0 means every level. */
870
+ setGroupLevelCollapsed?(level: number, collapsed: boolean): Promise<void>;
871
+ /** Detail rows: expanding loads the content once and resolves when it is drawn. */
872
+ expandDetail?(id: string): Promise<void>;
873
+ collapseDetail?(id: string): Promise<void>;
874
+ toggleDetail?(id: string): Promise<void>;
875
+ getExpandedDetails?(): string[];
548
876
  /** Requires selection controls and multiple selection. Query/revision changes retire this scope; paging preserves it. */
549
877
  selectAllMatching?(): void;
550
878
  /** Selects/deselects the full accepted page, including records outside the mounted viewport. */
@@ -562,30 +890,55 @@ export interface DataGrid<Q extends AcceptedGridQuery = GridQuery> {
562
890
  prepareExport(options: GridExportOptions): GridExport<Q>;
563
891
  toCsv(options: GridExportOptions): Promise<string>;
564
892
  copy(options: GridExportOptions): Promise<string>;
565
- exportToHost(format: string, options: GridExportOptions): Promise<void>;
893
+ /** Runs onExport under the export safety timeout (timeoutMs, else exportTimeoutMs); page reads keep requestTimeoutMs. */
894
+ exportToHost(format: string, options: GridExportOptions & { timeoutMs?: number }): Promise<void>;
895
+ /** Captures now (invalid capture options throw synchronously), then runs a registered or configured export format (the same writers the Export menu lists) and downloads its file. */
896
+ exportAs(format: string, options: Omit<GridExportOptions, "onProgress" | "maxRows"> & ExportFormatOptions): Promise<ExportFormatResult>;
566
897
  /** Reporting runtime only. Complete bounded record snapshot; default maxRows is 1000, never silently truncates. */
567
898
  materializeExport?(options: GridExportOptions): Promise<GridExportSnapshot<Q>>;
568
899
  /** Reporting runtime only. Passes the same bounded snapshot to onPrint; never prints viewport DOM. */
569
900
  print?(options: GridExportOptions): Promise<void>;
570
901
  /** Details are opened for loaded records, by toolbar, double click or Ctrl/Command+Enter. */
571
902
  openDetails(id: string): Promise<void>;
903
+ /** The current formatting rules: the view's, or the ones set since. */
904
+ getFormatRules(): readonly GridFormatRule[];
905
+ /** Replaces the formatting rules after validating them as a whole (a bad set changes nothing); loads the formatting part on first use. */
906
+ setFormatRules(rules: readonly GridFormatRule[]): Promise<readonly GridFormatRule[]>;
907
+ /** Opens the rule editor; loads the formatting and rule editor parts on first use. */
908
+ openFormatRules(): Promise<void>;
909
+ /** Opens any record in the record drawer: a loaded row, one the grid scrolls to, or one its record source returns. */
910
+ openRecord(id: string, options?: OpenRecordOptions): Promise<void>;
572
911
  focus(): void;
573
912
  /** Reveals a zero-based position in the accepted result without taking DOM focus from a host drawer. */
574
913
  scrollToRow(index: number): Promise<void>;
575
914
  retry(): Promise<Q | void>;
915
+ /** Closes this grid's toolbar menus and column popover. To close the menus of every view, dispatch canopyx:closemenus on the document. */
916
+ closeMenus(): void;
576
917
  /** Repaints the accepted data without querying the source. */
577
918
  refresh(): void;
578
919
  /** Requests a fresh source revision with accepted filters and ordering, or initial definition criteria before acceptance. Traversal restarts at the first page. */
579
920
  reload(): Promise<Q>;
580
921
  destroy(): void;
581
922
  }
582
- /** Requires a browser DOM. The grid runtime also hosts both existing tree variants. */
583
- export function createDataGrid(host: HTMLElement, options: GridOptions): DataGrid;
584
- export function createDataGrid(host: HTMLElement, options: CursorGridOptions): DataGrid<GridCursorQuery>;
923
+ /**
924
+ * Requires a browser DOM. The grid runtime also hosts both existing tree variants. Name a row type to check columns, rows,
925
+ * facets, events and the live API against it: `createDataGrid<Server>(element, { view })`, where `Server extends GridRow`.
926
+ */
927
+ /** Keeps a type parameter from being inferred from an argument (the deferred form works on every supported compiler). */
928
+ type GridNamedRow<Row> = [Row][Row extends unknown ? 0 : never];
929
+ // The row type is named, never inferred from the options: without one, rows are plain GridRow.
930
+ export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement, options: GridOptions<GridNamedRow<Row>>): DataGrid<GridQuery, Row>;
931
+ export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement, options: CursorGridOptions<GridNamedRow<Row>>): DataGrid<GridCursorQuery, Row>;
932
+ /** A calendar from a list of events, with no grid schema to write: a preset (basic by default) and options over it (calendar.d.ts). */
933
+ export function createCalendar(host: HTMLElement, options?: CalendarOptions): DataGrid<GridQuery, GridRow>;
585
934
  /** Registers options before either a tree or flat-grid fragment mounts. */
586
935
  export function configure(viewId: string, options: HostOptions | GridHostOptions | GridHostOptions<GridCursorQuery>): () => void;
587
- /** Mounts tree and flat-grid fragments below root. Narrow a controller before using view-specific methods. */
936
+ /**
937
+ * Mounts tree and flat-grid fragments below root, or root itself when it is a fragment. Fragments marked
938
+ * `data-cx-mount="visible"` below root wait until they come near the viewport; passing such a fragment as root mounts it at once.
939
+ * Narrow a controller before using view-specific methods.
940
+ */
588
941
  export function mount(root?: ParentNode): Array<TreeGrid | DataGrid<AcceptedGridQuery>>;
589
942
 
590
- declare const gridRuntime: Omit<typeof CanopyX, "configure" | "mount"> & { createDataGrid: typeof createDataGrid; configure: typeof configure; mount: typeof mount };
943
+ declare const gridRuntime: Omit<typeof CanopyX, "configure" | "mount"> & { createDataGrid: typeof createDataGrid; createCalendar: typeof createCalendar; enhance: typeof enhance; grid: typeof grid; configure: typeof configure; mount: typeof mount };
591
944
  export default gridRuntime;