@evotecit/canopyx 0.1.3 → 0.2.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 (85) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +114 -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,7 +1,10 @@
1
1
  import type {
2
2
  CanopyNode, CanopyColumn, CanopyView, InlineView, SidecarView, RemoteView, CanopyLabels, CellValue, CheckIntent,
3
- CountsDocument, PageResult, SearchResult, ViewQuery, ReadonlyViewQuery, ContextAction
3
+ CountsDocument, PageResult, SearchResult, ViewQuery, ReadonlyViewQuery, ContextAction, TimeZone, TreeToolbarControls
4
4
  } from "./protocol.js";
5
+ import type { CanopyPlugin } from "./plugins.js";
6
+ import type { RecordPanelOptions } from "./records.js";
7
+ import type { TreeGrid } from "./index.js";
5
8
 
6
9
  export type Awaitable<T> = T | PromiseLike<T>;
7
10
  /** Query-aware hosts answer from one accepted dataset revision. */
@@ -62,19 +65,115 @@ export interface CellContext {
62
65
  }
63
66
  export interface LoadInfo { node: CanopyNode | null; count: number; total: number | null; ms: number; }
64
67
 
68
+ /** One exported cell: the typed value, the display text formatted at capture, and a semantic tone where the data declares one. */
69
+ export interface ExportCell { readonly value: CellValue; readonly text: string; readonly tone?: string; }
70
+ /** What every export capture offers a format writer. Tree captures (request.kind "tree") and flat-grid captures add their own fields. */
71
+ export interface ExportRequest {
72
+ readonly columns: readonly { readonly id: string; readonly title: string; readonly kind: "text" | "number" | "datetime" }[];
73
+ /** Exact captured record count, or null when a source cannot know it. */
74
+ readonly recordCount: number | null;
75
+ readonly values: "raw" | "display";
76
+ readonly timeZone: TimeZone;
77
+ readonly locale?: string;
78
+ readonly fileName?: string;
79
+ }
80
+ export interface ExportCapture {
81
+ readonly request: ExportRequest;
82
+ /** Each enumeration reads the captured rows afresh; the signal links with the writer's own. */
83
+ rows(options?: { signal?: AbortSignal }): AsyncIterable<{ readonly id: string; readonly cells: Readonly<Record<string, ExportCell>> }>;
84
+ }
85
+ export interface ExportFormatContext {
86
+ /** Aborts on cancellation, the export safety timeout or grid destruction. */
87
+ readonly signal: AbortSignal;
88
+ /** The suggested file name without the extension. */
89
+ readonly fileName: string;
90
+ /** The format's id. */
91
+ readonly format: string;
92
+ /** Reports written records (a nonnegative integer; other values are ignored); the Export menu shows them as progress. */
93
+ progress(completed: number): void;
94
+ }
95
+ /** A writer the Export menu lists after CSV, in the grid's own menu with its scope picker, progress and cancellation. */
96
+ export interface ExportFormat<C extends ExportCapture = ExportCapture> {
97
+ /** 1–32 lowercase letters, digits and hyphens, starting with a letter. "csv" is built in. */
98
+ readonly id: string;
99
+ /** The button text in the Export menu, such as "Download Excel". */
100
+ readonly label: string;
101
+ /** The file extension without the dot (1–16 letters or digits). */
102
+ readonly extension: string;
103
+ /** The Blob type for bytes a writer returns; default application/octet-stream. */
104
+ readonly mime?: string;
105
+ /** Returns the file: a Blob (a File names the download itself), or bytes typed with mime. */
106
+ write(capture: C, context: ExportFormatContext): Blob | ArrayBuffer | ArrayBufferView | PromiseLike<Blob | ArrayBuffer | ArrayBufferView>;
107
+ }
108
+ export interface ExportProgress { readonly phase: "reading" | "generating" | "ready"; readonly completed: number; readonly total: number | null; }
109
+ export interface ExportFormatOptions {
110
+ signal?: AbortSignal;
111
+ onProgress?: (progress: ExportProgress) => void;
112
+ /** Safety timeout for the writer, 1..2147483647 ms; overrides the grid's exportTimeoutMs (default 600000). */
113
+ timeoutMs?: number;
114
+ /** False returns the file without starting a browser download. Default true. */
115
+ download?: boolean;
116
+ }
117
+ export interface ExportFormatResult { readonly blob: Blob; readonly fileName: string; readonly format: string; }
118
+ export interface TreeExportOptions {
119
+ /** "visible" (default): the rows the reader can see, in order. "selected": the loaded selected nodes in tree order, including collapsed ones.
120
+ * "all": every node in the current sort order, the whole tree of an inline source and the loaded nodes of a lazy one. */
121
+ scope?: "visible" | "selected" | "all";
122
+ /** Defaults to the visible columns in their current order. */
123
+ columns?: string[];
124
+ values?: "raw" | "display";
125
+ signal?: AbortSignal;
126
+ }
127
+ export interface TreeExportRequest extends ExportRequest {
128
+ readonly kind: "tree";
129
+ readonly scope: "visible" | "selected" | "all";
130
+ readonly presentation: "text";
131
+ readonly revision: string | null;
132
+ readonly recordCount: number;
133
+ }
134
+ /** A record with its depth (level 1 is a root) and the id path from its root, ending with its own id. */
135
+ export interface TreeExportRow { readonly id: string; readonly level: number; readonly path: readonly string[]; readonly cells: Readonly<Record<string, ExportCell>>; }
136
+ export interface TreeExport extends ExportCapture {
137
+ readonly request: TreeExportRequest;
138
+ rows(options?: { signal?: AbortSignal }): AsyncIterable<TreeExportRow>;
139
+ }
140
+ /** The TrustedHTML type of the DOM library when it declares Trusted Types; otherwise never. */
141
+ export type HostTrustedHTML = typeof globalThis extends { TrustedHTML: { prototype: infer T } } ? T : never;
65
142
  /** Hooks stay with the host. Acceptance never makes the grid perform a write. */
66
- interface HostOptionsBase {
143
+ interface HostOptionsBase extends RecordPanelOptions {
67
144
  source?: DataSource;
68
145
  labels?: CanopyLabels;
69
146
  requestTimeoutMs?: number;
147
+ /** The formats the Export menu and exportAs offer (ids of registered formats or provider objects); absent offers the view's exportFormats, else every registered format. */
148
+ exportFormats?: readonly (string | ExportFormat)[];
149
+ /** False hides the tree toolbar; an object switches single controls off, overriding the view's toolbar switches. */
150
+ toolbar?: boolean | TreeToolbarOptions;
151
+ /** Host entries of the tree's more menu, run with the selected nodes; the host owns what they do. */
152
+ actions?: readonly TreeAction[];
153
+ /** Plugins for this tree only, after the page's (CanopyX.use); one replaces a page plugin of the same name. */
154
+ plugins?: readonly CanopyPlugin[];
155
+ /** Loads the link part when the tree is created and reports its selection as canopyx:linkchange (LinkChange). */
156
+ linkEvents?: boolean;
157
+ /** Safety timeout for export format writers, 1..2147483647 ms (default 600000). */
158
+ exportTimeoutMs?: number;
70
159
  retainedNodeLimit?: number;
71
160
  inactiveNodeLimit?: number;
72
161
  interactionTimeoutMs?: number;
73
162
  renderers?: Record<string, (node: CanopyNode, element: HTMLElement, context: CellContext) => boolean>;
74
163
  renderDetail?: (node: CanopyNode, element: HTMLElement) => boolean;
164
+ /**
165
+ * Sees the markup the host declared trusted before it is written: an html detail ("detail") or a view icon ("icon").
166
+ * Return a string, a TrustedHTML from the host's own Trusted Types policy, or a Node, which is inserted as it is (docs/security.md).
167
+ */
168
+ sanitizeHtml?: (html: string, context: { readonly kind: "detail" | "icon" }) => string | HostTrustedHTML | Node;
169
+ /** Replaces the tree's record drawer with the host's own view of the node (a frozen copy); restore returnFocus when it closes. */
170
+ onDetails?: (event: { node: Readonly<CanopyNode>; signal: AbortSignal; returnFocus: Element | null }) => void | PromiseLike<void>;
75
171
  onSelect?: (node: CanopyNode | null, nodes: CanopyNode[]) => void;
76
172
  onToggle?: (node: CanopyNode, expanded: boolean) => void;
77
173
  onLoad?: (node: CanopyNode | null, info: LoadInfo) => void;
174
+ /** Row activation (Enter on a node row, or a double click), with the tree tools part: runs before the tree's own action
175
+ * (select and toggle); returning false keeps that from running. The cancelable canopyx:rowActivate event carries { id, node, keyboard }. */
176
+ onRowActivate?: (node: CanopyNode, context: { readonly keyboard: boolean }) => boolean | void;
78
177
  isSelectable?: (node: CanopyNode) => boolean;
79
178
  beforeSelect?: (node: CanopyNode | null, nodes: CanopyNode[], context: SelectionContext) => boolean | void;
80
179
  isCheckable?: (node: CanopyNode) => boolean;
@@ -88,6 +187,16 @@ interface HostOptionsBase {
88
187
  canDrop?: (proposal: DropProposal) => boolean;
89
188
  isActionEnabled?: (node: CanopyNode, action: Readonly<ContextAction>) => boolean;
90
189
  }
190
+ /** Per-control tree toolbar switches; an absent switch keeps its control, and a control switched off is not built. */
191
+ export interface TreeToolbarOptions extends TreeToolbarControls {
192
+ /** The search box; the view's search options still decide whether there is one. */
193
+ search?: boolean;
194
+ }
195
+ /** A more-menu entry of a tree. */
196
+ export interface TreeAction {
197
+ label: string;
198
+ run(context: { nodes: CanopyNode[]; signal: AbortSignal; api: TreeGrid }): unknown;
199
+ }
91
200
  /** A custom deep-link scheme supplies its formatter and parser together. */
92
201
  export type HostOptions = HostOptionsBase & (
93
202
  | { formatLink: (path: string[], node: CanopyNode | null) => string | null;
@@ -107,3 +216,69 @@ export type NodeFilter = ((node: CanopyNode) => boolean) | {
107
216
  match(node: CanopyNode): boolean;
108
217
  keep?(node: CanopyNode): boolean;
109
218
  };
219
+
220
+ // ---------- linked views (docs/linked-views.md) ----------
221
+ /** A raw value an external filter or a link payload carries. */
222
+ export type LinkValue = string | number | boolean;
223
+ interface ExternalFilterText {
224
+ /** The chip shows this instead of the values. */
225
+ label?: string;
226
+ /** Who applied the filter, as the chip and announcements name it; defaults to the source id. */
227
+ sourceLabel?: string;
228
+ /** Keep the filter in the state saved views and the remembered state store; off by default. */
229
+ persist?: boolean;
230
+ }
231
+ /** What another view applies: 1 to 1000 values of a declared column, or a [from, to] range (either end null) of a number or date column. */
232
+ export type ExternalFilterInput = ExternalFilterText & (
233
+ | { field: string; values: readonly LinkValue[]; range?: never }
234
+ | { field: string; range: readonly [LinkValue, LinkValue | null] | readonly [null, LinkValue]; values?: never });
235
+ /** An applied external filter, as getExternalFilters and canopyx:externalfilter report it. */
236
+ export interface ExternalFilter {
237
+ readonly source: string;
238
+ readonly field: string;
239
+ readonly values?: readonly LinkValue[];
240
+ readonly range?: readonly [LinkValue | null, LinkValue | null];
241
+ readonly label?: string;
242
+ readonly sourceLabel: string;
243
+ readonly persist: boolean;
244
+ }
245
+ export interface HighlightOptions {
246
+ /** Scroll the first highlighted row into view (default true). */
247
+ scroll?: boolean;
248
+ /** Who asked, for the screen-reader announcement. */
249
+ source?: string;
250
+ }
251
+ /** The detail of canopyx:externalfilter: the filters now applied, and whether the reader removed one (a chip or Clear all). */
252
+ export interface ExternalFilterEvent {
253
+ readonly filters: readonly ExternalFilter[];
254
+ readonly reason: "api" | "user";
255
+ }
256
+ /**
257
+ * The detail of canopyx:linkchange, which a view with linkEvents dispatches: the reader's selection (select, with the row keys
258
+ * and each column's distinct values across the selected loaded rows) or a flat grid's own eq and in conditions (filter, by
259
+ * column), and clear when either became empty. External filters and highlights never report.
260
+ */
261
+ export interface LinkChange {
262
+ readonly view: string;
263
+ readonly action: "select" | "filter" | "clear";
264
+ readonly scope: "select" | "filter";
265
+ readonly keys: readonly string[];
266
+ readonly values: Readonly<Record<string, readonly LinkValue[]>>;
267
+ readonly label: string;
268
+ /** A calendar's selected day, slots or resource: the instants on its start column, [from, to). */
269
+ readonly range?: { readonly column: string; readonly from: string; readonly to: string } | null;
270
+ }
271
+ /** The link methods of trees and flat grids. The first call loads the link part; the getters answer without it. */
272
+ export interface LinkedView {
273
+ /** Replaces the external filters of one source (1 to 128 letters, digits, '.', ':', '_' or '-'); null removes them. Resolves once the view shows the result. */
274
+ setExternalFilter(source: string, filter: ExternalFilterInput | readonly ExternalFilterInput[] | null): Promise<boolean>;
275
+ /** Removes every external filter, or those of one source. */
276
+ clearExternalFilters(source?: string): Promise<boolean>;
277
+ getExternalFilters(): ExternalFilter[];
278
+ /** Marks the rows with these keys (at most 10000) without filtering; resolves with the number of keys. */
279
+ setHighlight(keys: readonly string[], options?: HighlightOptions): Promise<number>;
280
+ clearHighlight(): Promise<number>;
281
+ getHighlight(): string[];
282
+ /** Starts canopyx:linkchange on a view created without linkEvents (for hosts that link views after they mount). */
283
+ startLinkEvents(): Promise<void>;
284
+ }
@@ -1,16 +1,20 @@
1
1
  import type {
2
2
  CanopyBadge, CanopyNode, CanopyView, CanopyLabels, CellValue, CheckIntent, CheckState,
3
- CountsDocument, SortDirection, TimeZone, ViewQuery, ViewState, LegacyViewState, SearchResult, LabelName
3
+ CountsDocument, SortDirection, TimeZone, ViewQuery, ViewState, LegacyViewState, SearchResult, LabelName, ViewModeMethods
4
4
  } from "./protocol.js";
5
5
  import type {
6
6
  TreeGridOptions, HostOptions, Sources, ExpandPathOptions, NodeFilter, FilterOptions,
7
- InteractionOptions, DropPlacement
7
+ InteractionOptions, DropPlacement, ExportFormat, ExportFormatOptions, ExportFormatResult, TreeExport, TreeExportOptions, LinkedView
8
8
  } from "./hosts.js";
9
+ import type { CanopyPlugin, CanopyPartDefinition } from "./plugins.js";
9
10
  export * from "./protocol.js";
10
11
  export * from "./hosts.js";
12
+ export * from "./plugins.js";
13
+ export * from "./records.js";
14
+ import type { OpenRecordOptions } from "./records.js";
11
15
 
12
16
  /** One mounted grid. Every method owns presentation only; host hooks own domain writes. */
13
- export interface TreeGrid {
17
+ export interface TreeGrid extends LinkedView, ViewModeMethods {
14
18
  readonly element: HTMLElement;
15
19
  readonly view: CanopyView;
16
20
  readonly ready: Promise<void>;
@@ -26,6 +30,7 @@ export interface TreeGrid {
26
30
  setQuery(query: ViewQuery, options?: { signal?: AbortSignal }): Promise<boolean>;
27
31
  setColumnVisible(columnId: string, visible: boolean): void;
28
32
  setColumnWidth(columnId: string, width: number): void;
33
+ /** Pins the tree column to the left edge or lets it scroll with the other columns; remembered with the view state. */
29
34
  setTreeColumnPinned(pinned: boolean): void;
30
35
  setTimeZone(zone: TimeZone): void;
31
36
  getTimeZone(): TimeZone;
@@ -40,7 +45,32 @@ export interface TreeGrid {
40
45
  selectAll(options: { scope: "visible" | "loaded" }): boolean;
41
46
  focus(): void;
42
47
  visibleRows(): { node: CanopyNode; level: number; path: string[] }[];
48
+ /** The indented outline of the open rows that print uses; drawn by the tree tools part, so empty until ready. */
43
49
  toOutline(): string;
50
+ /** Closes this tree's toolbar menus and actions menu (none before ready). To close every view's menus, dispatch canopyx:closemenus on the document. */
51
+ closeMenus(): void;
52
+ /** Once ready (the tree tools part): captures the visible rows (default), the loaded selected nodes or all rows now, with level and id path. */
53
+ prepareExport(options?: TreeExportOptions): TreeExport;
54
+ /** Once ready: captures now, then runs a registered or configured export format and downloads its file. */
55
+ exportAs(format: string, options?: TreeExportOptions & ExportFormatOptions): Promise<ExportFormatResult>;
56
+ /** Once ready: names of the views saved for the session (with stateKey; the page only otherwise). */
57
+ getSavedViews(): string[];
58
+ /** Saves the current state under a name of 1 to 100 characters; at most 20 views. */
59
+ saveView(name: string): void;
60
+ applySavedView(name: string): Promise<boolean>;
61
+ removeSavedView(name: string): void;
62
+ /** Returns to the declared columns, sort, filters and expansion. */
63
+ resetView(): Promise<boolean>;
64
+ /** Names every column once; the tree column stays first. */
65
+ setColumnOrder(ids: string[]): void;
66
+ /** Returns columns to their declared order, widths, visibility and pins. */
67
+ resetColumns(): void;
68
+ /** Once ready: pins a column other than the tree column to the left (after the tree column) or right edge, or unpins it. */
69
+ setColumnPinned(columnId: string, pin: "left" | "right" | "none"): void;
70
+ /** Once ready: opens the record drawer of a visible node, its label and every other column (the phone "+N" button opens it too). */
71
+ openDetails(id: string): Promise<void>;
72
+ /** Opens a node in the record drawer, also one in a collapsed branch, or one the id path in options leads to. */
73
+ openRecord(id: string, options?: OpenRecordOptions): Promise<void>;
44
74
  getState(): ViewState;
45
75
  setState(state: ViewState | LegacyViewState): Promise<boolean>;
46
76
  search(query: string, limit?: number): Promise<SearchResult>;
@@ -66,11 +96,26 @@ export function createTreeGrid(host: HTMLElement, options: TreeGridOptions): Tre
66
96
  /** Configures future fragment mounts and returns a disposer for this registration. Requires a browser DOM. */
67
97
  export function configure(viewId: string, options: HostOptions): () => void;
68
98
  export function unconfigure(viewId: string): boolean;
69
- /** Mounts unmounted fragments below root. Requires a browser DOM. */
99
+ /**
100
+ * Mounts unmounted fragments below root, or root itself when it is a fragment. Fragments marked `data-cx-mount="visible"`
101
+ * below root wait until they come near the viewport; passing such a fragment as root mounts it at once. Requires a browser DOM.
102
+ */
70
103
  export function mount(root?: ParentNode): TreeGrid[];
71
104
  export const sources: Readonly<Sources>;
72
105
  export function badge(value: CanopyBadge): HTMLElement;
73
106
  export const labels: Readonly<Record<LabelName, string>>;
107
+ /** Registers an export format for every grid and tree on the page that does not choose its own; returns a disposer. */
108
+ export function registerExportFormat(format: ExportFormat): () => void;
109
+ /** Registers a plugin for every grid and tree created afterwards; returns a disposer. Throws for an invalid plugin or a name already registered. */
110
+ export function use(plugin: CanopyPlugin): () => void;
111
+ /** Registers the code of a part of the runtime; part scripts call it as they run (docs/plugins.md). */
112
+ export function registerPart(name: string, part: CanopyPartDefinition): void;
113
+ /**
114
+ * Sets the Content Security Policy nonce the runtime gives the part files and sidecar scripts it loads from now on, and that
115
+ * an embedded part's payload must carry to run. Without it the runtime uses the nonce of its own script element, then (for
116
+ * files) that of a nonced script on the page. Throws for a value that is not a nonempty string (docs/security.md).
117
+ */
118
+ export function setNonce(nonce: string): void;
74
119
 
75
120
  declare const CanopyX: Readonly<{
76
121
  protocol: typeof protocol;
@@ -81,5 +126,9 @@ declare const CanopyX: Readonly<{
81
126
  sources: typeof sources;
82
127
  badge: typeof badge;
83
128
  labels: typeof labels;
129
+ registerExportFormat: typeof registerExportFormat;
130
+ use: typeof use;
131
+ registerPart: typeof registerPart;
132
+ setNonce: typeof setNonce;
84
133
  }>;
85
134
  export default CanopyX;
@@ -0,0 +1,115 @@
1
+ // Live data of flat grids (docs/live-data.md): rows added, changed and removed in place, the stable order, flashing cells,
2
+ // the batched update rate, polling and the revision-aware refresh of hosted grids.
3
+ import type { GridRow, GridQuery, AcceptedGridQuery } from "./grid.js";
4
+
5
+ /** The live settings a grid definition can carry (the C# CanopyGridLive writes them). */
6
+ export interface GridLiveDefinition {
7
+ /** Rows whose sort keys change keep their places until the reader re-sorts (default true); false moves them at once. */
8
+ stableOrder?: boolean;
9
+ /** Changed cells flash briefly (a still outline with reduced motion); an object sets the duration, 100..10000 ms (default 1200). */
10
+ flash?: boolean | { durationMs?: number };
11
+ /** Changes arriving within this many milliseconds apply together, 0..60000 (default 0: at once). */
12
+ batchMs?: number;
13
+ /** Polls the source every pollMs, 250..86400000 (0 or absent: no polling). */
14
+ pollMs?: number;
15
+ /** Announce added and removed rows to screen readers, at most every two seconds (default true). */
16
+ announce?: boolean;
17
+ /** Skip polls while the page is hidden and catch up when it shows (default true). */
18
+ pauseWhenHidden?: boolean;
19
+ }
20
+
21
+ /** What a live load function answers: complete rows (a full replacement, diffed by id), a transaction, or nothing. */
22
+ export type GridLiveAnswer<Row extends GridRow = GridRow> = readonly Row[] | GridTransaction<Row> | null | undefined;
23
+
24
+ /** The host's live options: the definition's settings plus the load function an inline grid polls. */
25
+ export interface GridLiveOptions<Row extends GridRow = GridRow, Q extends AcceptedGridQuery = GridQuery> extends GridLiveDefinition {
26
+ /** An inline grid's source of fresh rows for refreshRows and polling. */
27
+ load?: (context: { signal: AbortSignal; query: Q; rows: number }) => GridLiveAnswer<Row> | PromiseLike<GridLiveAnswer<Row>>;
28
+ }
29
+
30
+ /** A row update: the id, and the cells to change (null clears a nullable cell); tone, className, fields and detail replace the row's when given. */
31
+ export interface GridRowUpdate<Row extends GridRow = GridRow> {
32
+ id: string;
33
+ cells?: { [Column in keyof Row["cells"]]?: Row["cells"][Column] };
34
+ tone?: string | null;
35
+ className?: string | null;
36
+ fields?: Row extends { fields?: infer Fields } ? Fields : Record<string, unknown>;
37
+ detail?: Row["detail"];
38
+ }
39
+
40
+ /**
41
+ * Changes to apply in place. Removes apply first, then adds (a row whose id the grid has replaces it), then updates. A
42
+ * gridTransaction document (protocol, type, view) is accepted too. At most 100,000 rows in all.
43
+ */
44
+ export interface GridTransaction<Row extends GridRow = GridRow> {
45
+ add?: readonly Row[];
46
+ update?: readonly GridRowUpdate<Row>[];
47
+ remove?: readonly (string | { id: string })[];
48
+ protocol?: "canopyx/2";
49
+ type?: "gridTransaction";
50
+ view?: string;
51
+ }
52
+
53
+ /** What a change did; canopyx:rowsChanged and onRowsChanged carry the same detail with its source. */
54
+ export interface GridLiveResult {
55
+ readonly added: readonly string[];
56
+ readonly updated: readonly string[];
57
+ readonly removed: readonly string[];
58
+ /** Updates and removes of ids the grid did not have, which were skipped. */
59
+ readonly missing: readonly string[];
60
+ /** Kept rows are out of their sorted places and the "Order changed" prompt shows. */
61
+ readonly orderChanged: boolean;
62
+ /** A cursor grid's source has a newer revision and the "New data" prompt shows. */
63
+ readonly newData: boolean;
64
+ readonly revision: string | null;
65
+ readonly source: "api" | "poll" | "refresh";
66
+ }
67
+
68
+ export interface GridLiveState {
69
+ readonly orderChanged: boolean;
70
+ readonly newData: boolean;
71
+ /** Changes waiting for the batch window. */
72
+ readonly pending: number;
73
+ readonly polling: boolean;
74
+ readonly revision: string | null;
75
+ }
76
+
77
+ export interface GridPollOptions<Row extends GridRow = GridRow, Q extends AcceptedGridQuery = GridQuery> {
78
+ /** 250..86400000; defaults to the live pollMs. */
79
+ intervalMs?: number;
80
+ /** Poll at once instead of after the first interval. */
81
+ immediate?: boolean;
82
+ pauseWhenHidden?: boolean;
83
+ /** An inline grid's load function (replaces live.load). */
84
+ load?: GridLiveOptions<Row, Q>["load"];
85
+ }
86
+
87
+ /** A running poll; destroying the grid stops it. */
88
+ export interface GridPoller {
89
+ pause(): void;
90
+ resume(): void;
91
+ stop(): void;
92
+ /** Polls now (or joins a poll that runs). */
93
+ refresh(): Promise<void>;
94
+ readonly paused: boolean;
95
+ readonly active: boolean;
96
+ }
97
+
98
+ /** Live data methods of a flat grid; they load the live part on first use. */
99
+ export interface GridLiveApi<Row extends GridRow = GridRow, Q extends AcceptedGridQuery = GridQuery> {
100
+ /** Applies changes in place (inline rows; a hosted grid takes updates of its loaded rows). Settles when the batch applies. */
101
+ applyTransaction(transaction: GridTransaction<Row>): Promise<GridLiveResult>;
102
+ addRows(rows: readonly Row[]): Promise<GridLiveResult>;
103
+ updateRows(rows: readonly GridRowUpdate<Row>[]): Promise<GridLiveResult>;
104
+ removeRows(ids: readonly (string | { id: string })[]): Promise<GridLiveResult>;
105
+ /** Inline grids: replaces every row, applying only the differences, so unchanged rows keep everything. */
106
+ setRows(rows: readonly Row[]): Promise<GridLiveResult>;
107
+ /** Applies changes waiting for the batch window now. */
108
+ flushTransactions(): Promise<GridLiveResult | undefined>;
109
+ /** A hosted grid's revision-aware refresh, or an inline grid's live load function. */
110
+ refreshRows(): Promise<GridLiveResult>;
111
+ /** Moves kept rows to their sorted places; shows a hosted grid's newest result, a cursor grid's first page. */
112
+ resort(): Promise<void>;
113
+ poll(options?: GridPollOptions<Row, Q>): Promise<GridPoller>;
114
+ getLiveState(): GridLiveState;
115
+ }