@table-bro/angular 1.1.0 → 1.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.
package/README.md CHANGED
@@ -1,11 +1,27 @@
1
1
  # @table-bro/angular
2
2
 
3
- The Angular wrapper of [Table, Bro](https://table-bro.com): the `<table-bro-ng>` component with signal inputs and outputs
4
- over the `<table-bro>` Custom Element.
3
+ The Angular wrapper of [Table, Bro](https://table-bro.com), pixel-perfect Material (M3) data tables for Angular: the
4
+ `<table-bro-ng>` component with signal inputs and outputs over the `<table-bro>` Custom Element.
5
5
 
6
6
  The wrapper does not depend on a skin. It works with any registered skin of Table, Bro: Angular Material
7
7
  (`@table-bro/angular-material`), Bootstrap (`@table-bro/bootstrap`) or native (`@table-bro/native`).
8
8
 
9
+ ## With an AI coding agent
10
+
11
+ `<table-bro-ng>` is the table that the agent adds to an Angular project: a pixel-perfect Material (M3) data table with
12
+ search, paging, sorting and row details. Tell your AI assistant to use Table, Bro, or add its MCP server to Claude Code,
13
+ Cursor or Codex: the agent installs the packages, generates the table, its setup and its cells from one request, and
14
+ checks the result.
15
+
16
+ ```sh
17
+ claude mcp add --scope project table-bro -- npx -y @table-bro/mcp@1.2.0
18
+ ```
19
+
20
+ The setup for Cursor, Codex and VS Code is in the README of
21
+ [`@table-bro/mcp`](https://github.com/table-bro/table-bro/tree/main/packages/mcp) and on [MCP
22
+ server](https://table-bro.com/docs/ai/mcp-server). Docs: [Getting started](https://table-bro.com/docs/getting-started),
23
+ [Docs](https://table-bro.com/docs), [Material Design](https://table-bro.com/docs/design/material-design).
24
+
9
25
  ## Install
10
26
 
11
27
  ```sh
@@ -51,6 +67,9 @@ export class OrdersComponent {
51
67
  }
52
68
  ```
53
69
 
70
+ During Angular server-side rendering `<table-bro-ng>` prerenders the table in its loading state with the same template:
71
+ [Prerendering](https://table-bro.com/docs/ssr/prerendering).
72
+
54
73
  ### The server mode
55
74
 
56
75
  With `server: true` the table sorts, filters and pages nothing itself: it gives every gesture of the user as a
@@ -1,7 +1,160 @@
1
1
  import { isPlatformBrowser } from "@angular/common";
2
2
  import * as i0 from "@angular/core";
3
- import { ChangeDetectionStrategy, Component, ElementRef, NgZone, PLATFORM_ID, effect, inject, input, output, untracked } from "@angular/core";
4
- import { dispatchAction, events } from "@table-bro/core";
3
+ import { ANIMATION_MODULE_TYPE, ChangeDetectionStrategy, Component, DestroyRef, ElementRef, EnvironmentInjector, InjectionToken, Injector, NgZone, PLATFORM_ID, createComponent, effect, inject, input, output, reflectComponentType, untracked } from "@angular/core";
4
+ import { dispatchAction, events, getSkin, prerenderTableBro } from "@table-bro/core";
5
+ const TABLE_BRO_ELEMENTS = new InjectionToken("TABLE_BRO_ELEMENTS", { factory: () => [] });
6
+ function prerenderTableBroNg(host, cols, conf, elements, injector, tableId, theme) {
7
+ const tableBro = host.ownerDocument.createElement("table-bro");
8
+ if (tableId) tableBro.setAttribute("id", tableId);
9
+ if (theme) tableBro.setAttribute("theme", theme);
10
+ const customElementsDefined = withParentNodeMethods(() => withCustomElements(host.ownerDocument, () => {
11
+ host.replaceChildren(tableBro);
12
+ prerenderTableBro(tableBro, {
13
+ cols,
14
+ conf
15
+ });
16
+ }));
17
+ const selector = Object.keys(elements).join(",");
18
+ if (!selector) {
19
+ if (!customElementsDefined) host.replaceChildren();
20
+ return [];
21
+ }
22
+ const rendered = /* @__PURE__ */ new Map();
23
+ const elementInjector = Injector.create({ providers: [{
24
+ provide: ANIMATION_MODULE_TYPE,
25
+ useValue: "BrowserAnimations"
26
+ }] });
27
+ let pending;
28
+ while ((pending = Array.from(tableBro.querySelectorAll(selector)).filter((element) => !rendered.has(element))).length) pending.forEach((element) => rendered.set(element, renderElement(element, elements[element.localName], injector, elementInjector)));
29
+ return [...rendered.values()];
30
+ }
31
+ function renderElement(element, component, environmentInjector, elementInjector) {
32
+ const componentRef = createComponent(component, {
33
+ environmentInjector,
34
+ elementInjector,
35
+ hostElement: element,
36
+ projectableNodes: [Array.from(element.childNodes)]
37
+ });
38
+ reflectComponentType(component)?.inputs.forEach(({ propName, templateName }) => {
39
+ const attribute = templateName.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
40
+ if (Object.hasOwn(element, propName)) componentRef.setInput(templateName, element[propName]);
41
+ else if (element.hasAttribute(attribute)) componentRef.setInput(templateName, element.getAttribute(attribute));
42
+ });
43
+ componentRef.changeDetectorRef.detectChanges();
44
+ return componentRef;
45
+ }
46
+ const parentNodeMethods = {
47
+ append(...nodes) {
48
+ nodes.forEach((node) => this.appendChild(toNode(this, node)));
49
+ },
50
+ prepend(...nodes) {
51
+ const first = this.firstChild;
52
+ nodes.forEach((node) => this.insertBefore(toNode(this, node), first));
53
+ },
54
+ replaceChildren(...nodes) {
55
+ while (this.firstChild) this.removeChild(this.firstChild);
56
+ nodes.forEach((node) => this.appendChild(toNode(this, node)));
57
+ }
58
+ };
59
+ function withParentNodeMethods(render) {
60
+ const prototypes = [
61
+ Element,
62
+ DocumentFragment,
63
+ Document
64
+ ].map(({ prototype }) => prototype).filter((prototype) => !("append" in prototype));
65
+ prototypes.forEach((prototype) => Object.assign(prototype, parentNodeMethods));
66
+ try {
67
+ return render();
68
+ } finally {
69
+ prototypes.forEach((prototype) => Object.keys(parentNodeMethods).forEach((name) => delete prototype[name]));
70
+ }
71
+ }
72
+ const nodeInserted = 6;
73
+ function withCustomElements(document, render) {
74
+ const window = document.defaultView;
75
+ if (!window || window.customElements) {
76
+ render();
77
+ return !!window;
78
+ }
79
+ const definitions = /* @__PURE__ */ new Map();
80
+ const upgraded = /* @__PURE__ */ new Map();
81
+ const bases = /* @__PURE__ */ new Map();
82
+ const mutationHandler = document.mutationHandler;
83
+ const upgrade = (element) => {
84
+ const constructor = definitions.get(element.localName);
85
+ if (constructor && !upgraded.has(element)) {
86
+ upgraded.set(element, Object.getPrototypeOf(element));
87
+ construct(element, constructor, bases);
88
+ }
89
+ return element;
90
+ };
91
+ const customElementsOf = (node) => {
92
+ const elements = node instanceof Element ? [node] : [];
93
+ if (node instanceof Element || node instanceof DocumentFragment) elements.push(...Array.from(node.querySelectorAll("*")));
94
+ return elements.filter((element) => definitions.has(element.localName)).map(upgrade);
95
+ };
96
+ window.customElements = {
97
+ define: (name, constructor) => definitions.set(name, constructor),
98
+ get: (name) => definitions.get(name),
99
+ whenDefined: (name) => definitions.has(name) ? Promise.resolve(definitions.get(name)) : new Promise(() => void 0)
100
+ };
101
+ Object.defineProperties(document, {
102
+ createElement: {
103
+ configurable: true,
104
+ value: (tagName, options) => upgrade(Document.prototype.createElement.call(document, tagName, options))
105
+ },
106
+ adoptNode: {
107
+ configurable: true,
108
+ value: (node) => {
109
+ const parsed = node.ownerDocument !== document;
110
+ Document.prototype.adoptNode.call(document, node);
111
+ if (parsed) customElementsOf(node);
112
+ return node;
113
+ }
114
+ }
115
+ });
116
+ document.mutationHandler = (mutation) => {
117
+ mutationHandler?.call(document, mutation);
118
+ if (mutation.type === nodeInserted && mutation.node) customElementsOf(mutation.node).forEach((element) => element.connectedCallback?.());
119
+ };
120
+ try {
121
+ render();
122
+ return definitions.size > 0;
123
+ } finally {
124
+ upgraded.forEach((prototype, element) => Object.setPrototypeOf(element, prototype));
125
+ bases.forEach((prototype, basePrototype) => Object.setPrototypeOf(basePrototype, prototype));
126
+ delete window.customElements;
127
+ delete document.createElement;
128
+ delete document.adoptNode;
129
+ document.mutationHandler = mutationHandler;
130
+ }
131
+ }
132
+ function construct(element, constructor, bases) {
133
+ let derived = constructor;
134
+ let base = Object.getPrototypeOf(derived);
135
+ while (base !== Function.prototype && Object.getPrototypeOf(base) !== Function.prototype) {
136
+ derived = base;
137
+ base = Object.getPrototypeOf(base);
138
+ }
139
+ if (base === Function.prototype) return;
140
+ const basePrototype = base.prototype;
141
+ if (!(element instanceof base) && !bases.has(basePrototype)) {
142
+ bases.set(basePrototype, Object.getPrototypeOf(basePrototype));
143
+ Object.setPrototypeOf(basePrototype, Object.getPrototypeOf(element));
144
+ }
145
+ Object.setPrototypeOf(element, constructor.prototype);
146
+ Object.setPrototypeOf(derived, function upgradedElement() {
147
+ return element;
148
+ });
149
+ try {
150
+ Reflect.construct(constructor, []);
151
+ } finally {
152
+ Object.setPrototypeOf(derived, base);
153
+ }
154
+ }
155
+ function toNode(parent, node) {
156
+ return typeof node === "string" ? (parent.ownerDocument ?? parent).createTextNode(node) : node;
157
+ }
5
158
  var TableBroNg = class TableBroNg {
6
159
  constructor() {
7
160
  this.cols = input.required(...ngDevMode ? [{ debugName: "cols" }] : /* istanbul ignore next */ []);
@@ -21,7 +174,20 @@ var TableBroNg = class TableBroNg {
21
174
  this.appliedFailed = false;
22
175
  this.elementRef = inject(ElementRef);
23
176
  this.ngZone = inject(NgZone);
24
- if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
177
+ if (!isPlatformBrowser(inject(PLATFORM_ID))) {
178
+ const skins = inject(TABLE_BRO_ELEMENTS);
179
+ const injector = inject(EnvironmentInjector);
180
+ let components = [];
181
+ const destroyComponents = () => components.forEach((component) => component.destroy());
182
+ inject(DestroyRef).onDestroy(destroyComponents);
183
+ effect(() => {
184
+ const conf = this.conf();
185
+ const elements = skins.find(({ skin }) => skin === conf.skin)?.elements;
186
+ destroyComponents();
187
+ components = elements || conf.skin && getSkin(conf.skin)?.defineElements ? prerenderTableBroNg(this.elementRef.nativeElement, this.cols(), conf, elements ?? {}, injector, this.tableId(), this.theme()) : [];
188
+ });
189
+ return;
190
+ }
25
191
  effect(() => {
26
192
  const cols = this.cols();
27
193
  const conf = this.conf();
@@ -54,7 +220,8 @@ var TableBroNg = class TableBroNg {
54
220
  createTableBro(cols, conf, tableId) {
55
221
  const rows = this.rows();
56
222
  const theme = this.theme();
57
- this.tableBro = document.createElement("table-bro");
223
+ const prerendered = !this.tableBro && this.elementRef.nativeElement.querySelector(":scope > table-bro");
224
+ this.tableBro = prerendered || document.createElement("table-bro");
58
225
  this.appliedLoading = false;
59
226
  this.appliedFailed = false;
60
227
  if (tableId) this.tableBro.setAttribute("id", tableId);
@@ -68,7 +235,7 @@ var TableBroNg = class TableBroNg {
68
235
  this.tableBro.addEventListener(events.actionEvent, (event) => {
69
236
  if (event.target.closest("table-bro") === this.tableBro) this.tableBroListener(event);
70
237
  });
71
- this.elementRef.nativeElement.replaceChildren(this.tableBro);
238
+ if (!prerendered) this.elementRef.nativeElement.replaceChildren(this.tableBro);
72
239
  this.appliedFilter = void 0;
73
240
  this.applyFilter(this.filter());
74
241
  this.deliverRows(rows);
@@ -341,6 +508,6 @@ i0.ɵɵngDeclareClassMetadata({
341
508
  }]
342
509
  }
343
510
  });
344
- export { TableBroNg };
511
+ export { TABLE_BRO_ELEMENTS, TableBroNg };
345
512
 
346
513
  //# sourceMappingURL=table-bro-angular.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"table-bro-angular.mjs","names":[],"sources":["../../../packages/angular/src/lib/table-bro-ng.ts"],"sourcesContent":["import {isPlatformBrowser} from '@angular/common';\nimport {ChangeDetectionStrategy, Component, effect, ElementRef, inject, input, NgZone, output, PLATFORM_ID, untracked} from '@angular/core';\n\nimport {type BroActionDetail, type BroCol, type BroConfig, type BroEmptyState, type BroFilters, type BroQuery, type BroUserTheme, dispatchAction, events, type TableBroHTMLElement} from '@table-bro/core';\n\nimport type {BroServerFacade} from './table-bro.facade';\n\n/**\n * The Angular wrapper of <table-bro>: it translates the inputs into the methods of the element and the bro-action event\n * into the outputs. The table stays one: the wrapper renders nothing of its own and keeps no pagination, sort or filters.\n * The table works outside the Angular zone, so its animation frames start no change detection; the outputs emit inside it.\n */\n@Component({\n selector: 'table-bro-ng',\n template: '',\n styles: ':host { display: block; }',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TableBroNg<MODEL extends object> implements BroServerFacade<MODEL> {\n cols = input.required<BroCol<MODEL>[]>();\n /** The rows of the table; without a value, as before the first value of an async pipe, the table keeps what it shows. */\n rows = input.required<MODEL[] | undefined | null>();\n conf = input.required<BroConfig<MODEL>>();\n\n total = input<number>();\n rowsToAppend = input<MODEL[] | undefined>();\n /** The loading of the rows: the loading state of a table without rows, a refresh over the rows that the table shows. */\n loading = input<boolean>(false);\n /** A failed loading of the rows: the fail state of the table. */\n failed = input<boolean>(false);\n\n /**\n * The filters of the page outside the table, for example of a filter drawer. The table takes a new value as a change\n * of its own filters: it filters its rows in the local mode and sends a query with them in the server mode.\n * A cleared filter comes as undefined, null or an empty string; false and 0 are values of a filter.\n */\n filter = input<BroFilters<MODEL> | undefined>();\n /** The state of the table for empty rows: 'not-found' for an empty answer to a query with filters; 'no-data' without it. */\n emptyState = input<BroEmptyState>();\n /** The id of the <table-bro> element; without it the table gives itself a unique one. */\n tableId = input<string>();\n /** The theme of this table; without it the table takes the theme from its config. */\n theme = input<BroUserTheme | null>();\n\n action = output<BroActionDetail<MODEL>>();\n query = output<BroQuery<MODEL>>();\n\n /* Helpers */\n /** The loading that the table has got from the wrapper: its end without new rows ends the refresh. */\n private appliedLoading = false;\n /** The failure that the table shows: its end shows the rows of the page again. */\n private appliedFailed = false;\n /** The filters of the page the table has got from the wrapper: a new table gets the current ones again. */\n private appliedFilter: BroFilters<MODEL> | undefined;\n /** The rows the table has got from the wrapper: the same array is not delivered twice. */\n private deliveredRows: MODEL[] | undefined | null;\n private tableBro!: TableBroHTMLElement;\n\n private elementRef: ElementRef<HTMLElement> = inject(ElementRef);\n private ngZone = inject(NgZone);\n\n constructor() {\n // The server render of Angular leaves the host empty: the table is created in the browser\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n return;\n }\n\n // The element takes the columns and the config only at configure, so their change creates a new table\n effect(() => {\n const cols = this.cols();\n const conf = this.conf();\n const tableId = this.tableId();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.createTableBro(cols, conf, tableId)));\n });\n\n effect(() => {\n const rows = this.rows();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.deliverRows(rows)));\n });\n\n effect(() => {\n const rowsToAppend = this.rowsToAppend();\n\n if (!rowsToAppend) {\n return;\n }\n\n // One batch for one delivery: the rows go with the total of the same answer; their page is the one of the last query.\n // The server mode binds the total with the rows it appends\n this.ngZone.runOutsideAngular(() => this.tableBro.appendRows(rowsToAppend, {total: untracked(this.total)!}));\n });\n\n // The loading and the failure in one effect: the end of a failure shows the rows again before a loading goes on\n effect(() => {\n const failed = this.failed();\n const loading = this.loading();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.applyState(failed, loading)));\n });\n\n effect(() => {\n const filter = this.filter();\n\n this.ngZone.runOutsideAngular(() => this.applyFilter(filter));\n });\n\n effect(() => {\n const theme = this.theme();\n\n if (theme !== undefined) {\n this.ngZone.runOutsideAngular(() => this.tableBro.theme = theme);\n }\n });\n }\n\n private createTableBro(cols: BroCol<MODEL>[], conf: BroConfig<MODEL>, tableId?: string) {\n const rows = this.rows();\n const theme = this.theme();\n\n this.tableBro = document.createElement('table-bro') as TableBroHTMLElement;\n this.appliedLoading = false;\n this.appliedFailed = false;\n\n if (tableId) {\n this.tableBro.setAttribute('id', tableId);\n }\n\n if (theme !== undefined) {\n this.tableBro.theme = theme;\n }\n\n // The rows of the local mode render with the table; the server mode delivers them below, with their total\n this.deliveredRows = (!conf.server && rows?.length) ? rows : undefined;\n\n this.tableBro.configure({cols, rows: this.deliveredRows, conf});\n // The actions of a table in the row details of this one bubble through it: its outputs give only the own ones\n this.tableBro.addEventListener(events.actionEvent, (event: CustomEvent<BroActionDetail<MODEL>>) => {\n if ((<Element>event.target).closest('table-bro') === this.tableBro) {\n this.tableBroListener(event);\n }\n });\n\n this.elementRef.nativeElement.replaceChildren(this.tableBro);\n\n this.appliedFilter = undefined;\n this.applyFilter(this.filter());\n\n this.deliverRows(rows);\n\n // A new table takes the state of the page too\n if (this.failed()) {\n this.fail();\n } else {\n this.applyLoading(this.loading());\n }\n }\n\n private applyFilter(filter: BroFilters<MODEL> | undefined) {\n if (!filter || filter === this.appliedFilter) {\n return;\n }\n\n this.appliedFilter = filter;\n\n dispatchAction(this.tableBro, events.filtersFilterEvent, filter);\n }\n\n private deliverRows(rows: MODEL[] | undefined | null) {\n // Rows without a value change nothing: the loading and the failure come with their own inputs\n if (!rows || rows === this.deliveredRows) {\n return;\n }\n\n this.deliveredRows = rows;\n\n // The rows answer the last query of the table, which gives their page size\n this.tableBro.replaceRows(rows, {total: this.total(), emptyState: this.emptyState()});\n }\n\n /** A loading over the rows that the page still gives is a refresh; without rows it is the loading state. */\n private applyLoading(loading: boolean) {\n if (loading && this.rows()?.length) {\n this.tableBro.setState('refreshing');\n } else if (loading) {\n this.tableBro.setState('loading');\n // The table shows no rows now: the next rows are delivered, even the same array\n this.deliveredRows = undefined;\n } else if (this.appliedLoading) {\n this.tableBro.setState('idle');\n }\n\n this.appliedLoading = loading;\n }\n\n private fail() {\n this.tableBro.setState('fail');\n this.appliedFailed = true;\n // The table shows no rows now: the next rows are delivered, even the same array\n this.deliveredRows = undefined;\n }\n\n /**\n * The failure, else the loading. The end of a failure shows the rows of the page again, also the same array, which no\n * new value of the rows brings; a loading that goes on keeps its refresh over them.\n */\n private applyState(failed: boolean, loading: boolean) {\n if (failed) {\n if (!this.appliedFailed) {\n this.fail();\n }\n\n return;\n }\n\n if (this.appliedFailed) {\n this.appliedFailed = false;\n this.deliverRows(this.rows());\n }\n\n this.applyLoading(loading);\n }\n\n private tableBroListener(event: CustomEvent<BroActionDetail<MODEL>>) {\n const detail = event.detail;\n\n /**\n * Pagination, Sorting or Filters change\n */\n if (detail.action === events.filtersServerLoadedEvent) {\n const query = <BroQuery<MODEL>>detail.data;\n\n this.ngZone.run(() => this.query.emit(query));\n } else {\n this.ngZone.run(() => this.action.emit(detail));\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"table-bro-angular.mjs","names":[],"sources":["../../../packages/angular/src/lib/table-bro-prerender.ts","../../../packages/angular/src/lib/table-bro-ng.ts"],"sourcesContent":["import {ANIMATION_MODULE_TYPE, type ComponentRef, createComponent, type EnvironmentInjector, InjectionToken, Injector, reflectComponentType, type Type} from '@angular/core';\n\nimport {type BroCol, type BroConfig, type BroSkin, prerenderTableBro} from '@table-bro/core';\n\n/** The elements of a skin of Angular components: the component of each tag, as the skin defines it as a custom element. */\nexport interface TableBroSkinElements {\n /** The name of the skin. */\n readonly skin: BroSkin;\n /** The component of each tag. */\n readonly elements: Record<string, Type<unknown>>;\n}\n\n/**\n * The elements of the skins for the prerender. The server DOM of Angular has no custom elements, so the prerender renders\n * the components of the elements into the elements of the table itself; a skin of Angular components provides them, as\n * provideTableBroMatSkin() does. A skin of custom elements needs none: the prerender gives the server DOM a registry of\n * custom elements, where the skin defines its elements (BroSkinConfig.defineElements). A table of a skin with neither\n * keeps an empty host on the server.\n */\nexport const TABLE_BRO_ELEMENTS = new InjectionToken<TableBroSkinElements[]>('TABLE_BRO_ELEMENTS', {factory: () => []});\n\n/**\n * Renders the table into the host of the wrapper in the server DOM of Angular, in its loading state: the core renders the\n * regions of the table, and the components of the skin render into its elements, as the custom elements of the skin do in\n * the browser; the custom elements of a skin of custom elements render themselves. The host of a skin with neither stays\n * empty. The browser takes the element of the table over (TableBroNg). Returns the components, which the wrapper destroys\n * with itself: they belong to no view of the application.\n * @param host - the host of the wrapper in the server DOM\n * @param cols - the columns of the table\n * @param conf - the config of the table\n * @param elements - the components of the elements of the skin by their tags, none for a skin of custom elements\n * @param injector - the injector of the application, for the components\n * @param tableId - the id of the <table-bro> element\n * @param theme - the theme of the table\n */\nexport function prerenderTableBroNg<MODEL extends object>(host: HTMLElement, cols: BroCol<MODEL>[], conf: BroConfig<MODEL>, elements: Record<string, Type<unknown>>, injector: EnvironmentInjector, tableId?: string, theme?: string | null): ComponentRef<unknown>[] {\n const tableBro = host.ownerDocument.createElement('table-bro');\n\n if (tableId) {\n tableBro.setAttribute('id', tableId);\n }\n\n if (theme) {\n tableBro.setAttribute('theme', theme);\n }\n\n const customElementsDefined = withParentNodeMethods(() => withCustomElements(<ServerDocument>host.ownerDocument, () => {\n host.replaceChildren(tableBro);\n prerenderTableBro(tableBro, {cols, conf});\n }));\n const selector = Object.keys(elements).join(',');\n\n // A skin of custom elements has rendered them with the table\n if (!selector) {\n if (!customElementsDefined) {\n host.replaceChildren();\n }\n\n return [];\n }\n\n // The components render their templates, which may hold elements of the skin too: they render after them\n const rendered = new Map<Element, ComponentRef<unknown>>();\n // The components render the markup of the browser, with its animations, as the progress bar of Material: the server\n // render of Angular has no animations, and the CSS animations of the prerender run from its first paint\n const elementInjector = Injector.create({providers: [{provide: ANIMATION_MODULE_TYPE, useValue: 'BrowserAnimations'}]});\n let pending: Element[];\n\n while ((pending = Array.from(tableBro.querySelectorAll(selector)).filter(element => !rendered.has(element))).length) {\n pending.forEach(element => rendered.set(element, renderElement(element, elements[element.localName], injector, elementInjector)));\n }\n\n return [...rendered.values()];\n}\n\n/**\n * Renders a component into an element of the table, as an Angular element does: the content of the element goes into\n * the content of the component, and its inputs take the properties that the table set on the element, else its\n * attributes, by the names of the inputs in dashes.\n * @param element - the element of the skin\n * @param component - the component of the element\n * @param environmentInjector - the injector of the application\n * @param elementInjector - the injector of the elements, with the animations of the browser\n */\nfunction renderElement(element: Element, component: Type<unknown>, environmentInjector: EnvironmentInjector, elementInjector: Injector): ComponentRef<unknown> {\n const componentRef = createComponent(component, {environmentInjector, elementInjector, hostElement: element, projectableNodes: [Array.from(element.childNodes)]});\n\n reflectComponentType(component)?.inputs.forEach(({propName, templateName}) => {\n const attribute = templateName.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`);\n\n if (Object.hasOwn(element, propName)) {\n componentRef.setInput(templateName, (<Record<string, unknown>><unknown>element)[propName]);\n } else if (element.hasAttribute(attribute)) {\n componentRef.setInput(templateName, element.getAttribute(attribute));\n }\n });\n\n componentRef.changeDetectorRef.detectChanges();\n\n return componentRef;\n}\n\n/** The methods of ParentNode that add nodes, by the DOM standard: a text is added as a text node. */\nconst parentNodeMethods = {\n append(this: ParentNode & Node, ...nodes: (Node | string)[]): void {\n nodes.forEach(node => this.appendChild(toNode(this, node)));\n },\n prepend(this: ParentNode & Node, ...nodes: (Node | string)[]): void {\n const first = this.firstChild;\n\n nodes.forEach(node => this.insertBefore(toNode(this, node), first));\n },\n replaceChildren(this: ParentNode & Node, ...nodes: (Node | string)[]): void {\n while (this.firstChild) {\n this.removeChild(this.firstChild);\n }\n\n nodes.forEach(node => this.appendChild(toNode(this, node)));\n }\n};\n\n/**\n * Runs the render of the core in the server DOM of Angular. The core creates its nodes in the document of the table and\n * adds them with the methods of ParentNode, which the server DOM of Angular lacks: its elements, fragments and documents\n * get them while the render runs, and lose them after it, so the classes of the server DOM stay as Angular made them for\n * the rest of the application and for the other renders of the process.\n * @param render - the render of the core\n */\nfunction withParentNodeMethods<T>(render: () => T): T {\n const prototypes = [Element, DocumentFragment, Document].map(({prototype}) => prototype).filter(prototype => !('append' in prototype));\n\n prototypes.forEach(prototype => Object.assign(prototype, parentNodeMethods));\n\n try {\n return render();\n } finally {\n prototypes.forEach(prototype => Object.keys(parentNodeMethods).forEach(name => delete (<Record<string, unknown>>prototype)[name]));\n }\n}\n\n/** A document of the server DOM of Angular (domino), with its handler of the changes of the document, as an inserted node. */\ninterface ServerDocument extends Document {\n mutationHandler?: (mutation: {type: number; node?: Node}) => void;\n}\n\n/** The type of the change of the server DOM of Angular for a node inserted into its document. */\nconst nodeInserted = 6;\n\n/**\n * Runs the render of the core with a registry of custom elements in the window of the server DOM of Angular, which has\n * none, so that the elements of a skin of custom elements render as in the browser: the skin defines them in the registry\n * (BroSkinConfig.defineElements), the document creates them as instances of their classes, also in parsed HTML, and the\n * elements that the render inserts into the document connect. The registry, the creation and the connection belong to the document of the\n * render while it runs: the other renders of the process and the code of the application do not see them. Returns\n * whether the skin has defined elements.\n * @param document - the server document of the table\n * @param render - the render of the core\n */\nfunction withCustomElements(document: ServerDocument, render: () => void): boolean {\n // The window of the server DOM of Angular has no registry of custom elements\n const window = <{customElements?: CustomElementRegistry} | null>document.defaultView;\n\n // A server DOM with a registry of its own, or a document without a window\n if (!window || window.customElements) {\n render();\n\n return !!window;\n }\n\n const definitions = new Map<string, CustomElementConstructor>();\n // The upgraded elements with their prototypes of the server DOM, which they take back after the render\n const upgraded = new Map<Element, object>();\n // The prototypes of the empty base classes of the custom elements with their own prototypes (construct)\n const bases = new Map<object, object>();\n const mutationHandler = document.mutationHandler;\n\n // An element of a defined tag becomes an instance of its class once, as at its creation in the browser\n const upgrade = <T extends Element>(element: T): T => {\n const constructor = definitions.get(element.localName);\n\n if (constructor && !upgraded.has(element)) {\n upgraded.set(element, Object.getPrototypeOf(element));\n construct(element, constructor, bases);\n }\n\n return element;\n };\n\n // The custom elements of a node in the order of the tree, upgraded\n const customElementsOf = (node: Node): Element[] => {\n const elements = (node instanceof Element) ? [node] : [];\n\n if (node instanceof Element || node instanceof DocumentFragment) {\n elements.push(...Array.from(node.querySelectorAll('*')));\n }\n\n return elements.filter(element => definitions.has(element.localName)).map(upgrade);\n };\n\n window.customElements = <CustomElementRegistry><unknown>{\n define: (name: string, constructor: CustomElementConstructor) => definitions.set(name, constructor),\n get: (name: string) => definitions.get(name),\n whenDefined: (name: string) => definitions.has(name) ? Promise.resolve(definitions.get(name)) : new Promise(() => undefined)\n };\n\n // Own methods of the document, over the ones of the server DOM, which are read-only\n Object.defineProperties(document, {\n createElement: {\n configurable: true,\n value: (tagName: string, options?: ElementCreationOptions) => upgrade(Document.prototype.createElement.call(document, tagName, options))\n },\n // Parsed HTML comes from the document of the parser: its elements upgrade as the document adopts them, as the browser\n // creates the elements of the HTML of an element in the document of the element\n adoptNode: {\n configurable: true,\n value: <T extends Node>(node: T): T => {\n const parsed = (node.ownerDocument !== document);\n\n Document.prototype.adoptNode.call(document, node);\n\n if (parsed) {\n customElementsOf(node);\n }\n\n return node;\n }\n }\n });\n\n // The custom elements of a node inserted into the document connect\n document.mutationHandler = mutation => {\n mutationHandler?.call(document, mutation);\n\n if (mutation.type === nodeInserted && mutation.node) {\n customElementsOf(mutation.node).forEach(element => (<Partial<CustomElementCallbacks>>element).connectedCallback?.());\n }\n };\n\n try {\n render();\n\n return definitions.size > 0;\n } finally {\n // The document keeps the elements of the server DOM, without the classes of the skin\n upgraded.forEach((prototype, element) => Object.setPrototypeOf(element, prototype));\n bases.forEach((prototype, basePrototype) => Object.setPrototypeOf(basePrototype, prototype));\n delete window.customElements;\n delete (<Partial<Document>>document).createElement;\n delete (<Partial<Document>>document).adoptNode;\n document.mutationHandler = mutationHandler;\n }\n}\n\n/** The callback of a custom element that the prerender calls. */\ninterface CustomElementCallbacks {\n /** The element is connected to the document. */\n connectedCallback(): void;\n}\n\n/**\n * Runs the constructor of a custom element on an element of the server DOM, as the browser upgrades an element: the\n * element takes the prototype of the class, and the constructor runs with the element as this, so the fields of its\n * classes are set on it. For the time of the call, the class that extends the base class of the custom elements extends\n * a constructor that gives the element, as the HTMLElement of the browser gives the element that it upgrades. The base\n * class is HTMLElement of the server DOM, or an empty class, as BroHTMLElement where the core loads before the server\n * DOM: the prototype of an empty one takes the prototype of the element until the render ends, for the methods of the\n * element.\n * @param element - the element of the server DOM\n * @param constructor - the class of the custom element\n * @param bases - the prototypes of the empty base classes with their own prototypes, which they take back after the render\n */\nfunction construct(element: Element, constructor: CustomElementConstructor, bases: Map<object, object>): void {\n let derived: object = constructor;\n let base: object = Object.getPrototypeOf(derived);\n\n while (base !== Function.prototype && Object.getPrototypeOf(base) !== Function.prototype) {\n derived = base;\n base = Object.getPrototypeOf(base);\n }\n\n // A class that extends no class is no custom element\n if (base === Function.prototype) {\n return;\n }\n\n const basePrototype = (<{prototype: object}>base).prototype;\n\n if (!(element instanceof <CustomElementConstructor>base) && !bases.has(basePrototype)) {\n bases.set(basePrototype, Object.getPrototypeOf(basePrototype));\n Object.setPrototypeOf(basePrototype, Object.getPrototypeOf(element));\n }\n\n Object.setPrototypeOf(element, constructor.prototype);\n Object.setPrototypeOf(derived, function upgradedElement() {\n return element;\n });\n\n try {\n Reflect.construct(constructor, []);\n } finally {\n Object.setPrototypeOf(derived, base);\n }\n}\n\n/**\n * A node to add to a parent: a text becomes a text node of the document of the parent.\n * @param parent - the parent of the node\n * @param node - the node or the text\n */\nfunction toNode(parent: Node, node: Node | string): Node {\n return (typeof node === 'string') ? (<Document>(parent.ownerDocument ?? parent)).createTextNode(node) : node;\n}\n","import {isPlatformBrowser} from '@angular/common';\nimport {ChangeDetectionStrategy, Component, type ComponentRef, DestroyRef, effect, ElementRef, EnvironmentInjector, inject, input, NgZone, output, PLATFORM_ID, untracked} from '@angular/core';\n\nimport {type BroActionDetail, type BroCol, type BroConfig, type BroEmptyState, type BroFilters, type BroQuery, type BroUserTheme, dispatchAction, events, getSkin, type TableBroHTMLElement} from '@table-bro/core';\n\nimport {prerenderTableBroNg, TABLE_BRO_ELEMENTS} from './table-bro-prerender';\nimport type {BroServerFacade} from './table-bro.facade';\n\n/**\n * The Angular wrapper of <table-bro>: it translates the inputs into the methods of the element and the bro-action event\n * into the outputs. The table stays one: the wrapper renders nothing of its own and keeps no pagination, sort or filters.\n * The table works outside the Angular zone, so its animation frames start no change detection; the outputs emit inside it.\n * The server render of Angular renders the table in its loading state (prerenderTableBroNg), and the browser renders the\n * table in that element: the page shows the table before the application starts.\n */\n@Component({\n selector: 'table-bro-ng',\n template: '',\n styles: ':host { display: block; }',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class TableBroNg<MODEL extends object> implements BroServerFacade<MODEL> {\n cols = input.required<BroCol<MODEL>[]>();\n /** The rows of the table; without a value, as before the first value of an async pipe, the table keeps what it shows. */\n rows = input.required<MODEL[] | undefined | null>();\n conf = input.required<BroConfig<MODEL>>();\n\n total = input<number>();\n rowsToAppend = input<MODEL[] | undefined>();\n /** The loading of the rows: the loading state of a table without rows, a refresh over the rows that the table shows. */\n loading = input<boolean>(false);\n /** A failed loading of the rows: the fail state of the table. */\n failed = input<boolean>(false);\n\n /**\n * The filters of the page outside the table, for example of a filter drawer. The table takes a new value as a change\n * of its own filters: it filters its rows in the local mode and sends a query with them in the server mode.\n * A cleared filter comes as undefined, null or an empty string; false and 0 are values of a filter.\n */\n filter = input<BroFilters<MODEL> | undefined>();\n /** The state of the table for empty rows: 'not-found' for an empty answer to a query with filters; 'no-data' without it. */\n emptyState = input<BroEmptyState>();\n /** The id of the <table-bro> element; without it the table gives itself a unique one. */\n tableId = input<string>();\n /** The theme of this table; without it the table takes the theme from its config. */\n theme = input<BroUserTheme | null>();\n\n action = output<BroActionDetail<MODEL>>();\n query = output<BroQuery<MODEL>>();\n\n /* Helpers */\n /** The loading that the table has got from the wrapper: its end without new rows ends the refresh. */\n private appliedLoading = false;\n /** The failure that the table shows: its end shows the rows of the page again. */\n private appliedFailed = false;\n /** The filters of the page the table has got from the wrapper: a new table gets the current ones again. */\n private appliedFilter: BroFilters<MODEL> | undefined;\n /** The rows the table has got from the wrapper: the same array is not delivered twice. */\n private deliveredRows: MODEL[] | undefined | null;\n private tableBro!: TableBroHTMLElement;\n\n private elementRef: ElementRef<HTMLElement> = inject(ElementRef);\n private ngZone = inject(NgZone);\n\n constructor() {\n // The server render of Angular renders the table in its loading state; the browser renders it with its rows\n if (!isPlatformBrowser(inject(PLATFORM_ID))) {\n const skins = inject(TABLE_BRO_ELEMENTS);\n const injector = inject(EnvironmentInjector);\n let components: ComponentRef<unknown>[] = [];\n\n // The components of the elements of the skin belong to no view: they go with the table that they render\n const destroyComponents = () => components.forEach(component => component.destroy());\n\n inject(DestroyRef).onDestroy(destroyComponents);\n\n effect(() => {\n const conf = this.conf();\n const elements = skins.find(({skin}) => skin === conf.skin)?.elements;\n\n destroyComponents();\n // A skin of custom elements defines them in the server DOM; the host of a skin without elements for the server, or\n // of a skin that only the browser registers, stays empty\n components = (elements || (conf.skin && getSkin(conf.skin)?.defineElements)) ? prerenderTableBroNg(this.elementRef.nativeElement, this.cols(), conf, elements ?? {}, injector, this.tableId(), this.theme()) : [];\n });\n\n return;\n }\n\n // The element takes the columns and the config only at configure, so their change creates a new table\n effect(() => {\n const cols = this.cols();\n const conf = this.conf();\n const tableId = this.tableId();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.createTableBro(cols, conf, tableId)));\n });\n\n effect(() => {\n const rows = this.rows();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.deliverRows(rows)));\n });\n\n effect(() => {\n const rowsToAppend = this.rowsToAppend();\n\n if (!rowsToAppend) {\n return;\n }\n\n // One batch for one delivery: the rows go with the total of the same answer; their page is the one of the last query.\n // The server mode binds the total with the rows it appends\n this.ngZone.runOutsideAngular(() => this.tableBro.appendRows(rowsToAppend, {total: untracked(this.total)!}));\n });\n\n // The loading and the failure in one effect: the end of a failure shows the rows again before a loading goes on\n effect(() => {\n const failed = this.failed();\n const loading = this.loading();\n\n this.ngZone.runOutsideAngular(() => untracked(() => this.applyState(failed, loading)));\n });\n\n effect(() => {\n const filter = this.filter();\n\n this.ngZone.runOutsideAngular(() => this.applyFilter(filter));\n });\n\n effect(() => {\n const theme = this.theme();\n\n if (theme !== undefined) {\n this.ngZone.runOutsideAngular(() => this.tableBro.theme = theme);\n }\n });\n }\n\n private createTableBro(cols: BroCol<MODEL>[], conf: BroConfig<MODEL>, tableId?: string) {\n const rows = this.rows();\n const theme = this.theme();\n // The first table renders in the element of the prerender, in place of its loading state\n const prerendered = !this.tableBro && this.elementRef.nativeElement.querySelector(':scope > table-bro');\n\n this.tableBro = (prerendered || document.createElement('table-bro')) as TableBroHTMLElement;\n this.appliedLoading = false;\n this.appliedFailed = false;\n\n if (tableId) {\n this.tableBro.setAttribute('id', tableId);\n }\n\n if (theme !== undefined) {\n this.tableBro.theme = theme;\n }\n\n // The rows of the local mode render with the table; the server mode delivers them below, with their total\n this.deliveredRows = (!conf.server && rows?.length) ? rows : undefined;\n\n this.tableBro.configure({cols, rows: this.deliveredRows, conf});\n // The actions of a table in the row details of this one bubble through it: its outputs give only the own ones\n this.tableBro.addEventListener(events.actionEvent, (event: CustomEvent<BroActionDetail<MODEL>>) => {\n if ((<Element>event.target).closest('table-bro') === this.tableBro) {\n this.tableBroListener(event);\n }\n });\n\n // The element of the prerender is in its place already\n if (!prerendered) {\n this.elementRef.nativeElement.replaceChildren(this.tableBro);\n }\n\n this.appliedFilter = undefined;\n this.applyFilter(this.filter());\n\n this.deliverRows(rows);\n\n // A new table takes the state of the page too\n if (this.failed()) {\n this.fail();\n } else {\n this.applyLoading(this.loading());\n }\n }\n\n private applyFilter(filter: BroFilters<MODEL> | undefined) {\n if (!filter || filter === this.appliedFilter) {\n return;\n }\n\n this.appliedFilter = filter;\n\n dispatchAction(this.tableBro, events.filtersFilterEvent, filter);\n }\n\n private deliverRows(rows: MODEL[] | undefined | null) {\n // Rows without a value change nothing: the loading and the failure come with their own inputs\n if (!rows || rows === this.deliveredRows) {\n return;\n }\n\n this.deliveredRows = rows;\n\n // The rows answer the last query of the table, which gives their page size\n this.tableBro.replaceRows(rows, {total: this.total(), emptyState: this.emptyState()});\n }\n\n /** A loading over the rows that the page still gives is a refresh; without rows it is the loading state. */\n private applyLoading(loading: boolean) {\n if (loading && this.rows()?.length) {\n this.tableBro.setState('refreshing');\n } else if (loading) {\n this.tableBro.setState('loading');\n // The table shows no rows now: the next rows are delivered, even the same array\n this.deliveredRows = undefined;\n } else if (this.appliedLoading) {\n this.tableBro.setState('idle');\n }\n\n this.appliedLoading = loading;\n }\n\n private fail() {\n this.tableBro.setState('fail');\n this.appliedFailed = true;\n // The table shows no rows now: the next rows are delivered, even the same array\n this.deliveredRows = undefined;\n }\n\n /**\n * The failure, else the loading. The end of a failure shows the rows of the page again, also the same array, which no\n * new value of the rows brings; a loading that goes on keeps its refresh over them.\n */\n private applyState(failed: boolean, loading: boolean) {\n if (failed) {\n if (!this.appliedFailed) {\n this.fail();\n }\n\n return;\n }\n\n if (this.appliedFailed) {\n this.appliedFailed = false;\n this.deliverRows(this.rows());\n }\n\n this.applyLoading(loading);\n }\n\n private tableBroListener(event: CustomEvent<BroActionDetail<MODEL>>) {\n const detail = event.detail;\n\n /**\n * Pagination, Sorting or Filters change\n */\n if (detail.action === events.filtersServerLoadedEvent) {\n const query = <BroQuery<MODEL>>detail.data;\n\n this.ngZone.run(() => this.query.emit(query));\n } else {\n this.ngZone.run(() => this.action.emit(detail));\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@table-bro/angular",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Pixel-perfect Material data tables for Angular, and more. A Web Components table engine on top of existing UI components.",
5
5
  "keywords": [
6
6
  "angular",
@@ -8,7 +8,20 @@
8
8
  "data-table",
9
9
  "datatable",
10
10
  "data-grid",
11
- "web-components"
11
+ "web-components",
12
+ "mcp",
13
+ "ai-coding-agents",
14
+ "vibe-coding",
15
+ "claude-code",
16
+ "cursor",
17
+ "codex",
18
+ "github-copilot",
19
+ "windsurf",
20
+ "gemini-cli",
21
+ "grok",
22
+ "cline",
23
+ "zed",
24
+ "jetbrains-ai"
12
25
  ],
13
26
  "author": "table-bro",
14
27
  "homepage": "https://table-bro.com",
@@ -25,7 +38,7 @@
25
38
  "peerDependencies": {
26
39
  "@angular/common": ">=20 <23",
27
40
  "@angular/core": ">=20 <23",
28
- "@table-bro/core": "^1.1.0"
41
+ "@table-bro/core": "^1.2.0"
29
42
  },
30
43
  "dependencies": {
31
44
  "tslib": "^2.3.0"
@@ -1,6 +1,6 @@
1
- import { BroActionDetail, BroCol, BroConfig, BroEmptyState, BroFilters, BroQuery, BroUserTheme } from "@table-bro/core";
1
+ import { BroActionDetail, BroCol, BroConfig, BroEmptyState, BroFilters, BroQuery, BroSkin, BroUserTheme } from "@table-bro/core";
2
2
  import * as i0 from "@angular/core";
3
- import { InputSignal, OutputEmitterRef, Signal } from "@angular/core";
3
+ import { ComponentRef, EnvironmentInjector, InjectionToken, InputSignal, OutputEmitterRef, Signal, Type } from "@angular/core";
4
4
  /**
5
5
  * Public facade contract for a table component.
6
6
  *
@@ -98,6 +98,8 @@ export interface BroServerContainer<MODEL extends object, EXTRA extends object =
98
98
  * The Angular wrapper of <table-bro>: it translates the inputs into the methods of the element and the bro-action event
99
99
  * into the outputs. The table stays one: the wrapper renders nothing of its own and keeps no pagination, sort or filters.
100
100
  * The table works outside the Angular zone, so its animation frames start no change detection; the outputs emit inside it.
101
+ * The server render of Angular renders the table in its loading state (prerenderTableBroNg), and the browser renders the
102
+ * table in that element: the page shows the table before the application starts.
101
103
  */
102
104
  export declare class TableBroNg<MODEL extends object> implements BroServerFacade<MODEL> {
103
105
  cols: import("@angular/core").InputSignal<BroCol<MODEL>[]>;
@@ -210,6 +212,36 @@ export declare class TableBroNg<MODEL extends object> implements BroServerFacade
210
212
  "query": "query";
211
213
  }, never, never, true, never>;
212
214
  }
215
+ /** The elements of a skin of Angular components: the component of each tag, as the skin defines it as a custom element. */
216
+ interface TableBroSkinElements {
217
+ /** The name of the skin. */
218
+ readonly skin: BroSkin;
219
+ /** The component of each tag. */
220
+ readonly elements: Record<string, Type<unknown>>;
221
+ }
222
+ /**
223
+ * The elements of the skins for the prerender. The server DOM of Angular has no custom elements, so the prerender renders
224
+ * the components of the elements into the elements of the table itself; a skin of Angular components provides them, as
225
+ * provideTableBroMatSkin() does. A skin of custom elements needs none: the prerender gives the server DOM a registry of
226
+ * custom elements, where the skin defines its elements (BroSkinConfig.defineElements). A table of a skin with neither
227
+ * keeps an empty host on the server.
228
+ */
229
+ export declare const TABLE_BRO_ELEMENTS: InjectionToken<TableBroSkinElements[]>;
230
+ /**
231
+ * Renders the table into the host of the wrapper in the server DOM of Angular, in its loading state: the core renders the
232
+ * regions of the table, and the components of the skin render into its elements, as the custom elements of the skin do in
233
+ * the browser; the custom elements of a skin of custom elements render themselves. The host of a skin with neither stays
234
+ * empty. The browser takes the element of the table over (TableBroNg). Returns the components, which the wrapper destroys
235
+ * with itself: they belong to no view of the application.
236
+ * @param host - the host of the wrapper in the server DOM
237
+ * @param cols - the columns of the table
238
+ * @param conf - the config of the table
239
+ * @param elements - the components of the elements of the skin by their tags, none for a skin of custom elements
240
+ * @param injector - the injector of the application, for the components
241
+ * @param tableId - the id of the <table-bro> element
242
+ * @param theme - the theme of the table
243
+ */
244
+ declare function prerenderTableBroNg<MODEL extends object>(host: HTMLElement, cols: BroCol<MODEL>[], conf: BroConfig<MODEL>, elements: Record<string, Type<unknown>>, injector: EnvironmentInjector, tableId?: string, theme?: string | null): ComponentRef<unknown>[];
213
245
  /**
214
246
  * Service contract responsible for translating between the table domain model
215
247
  * and a specific backend API.
@@ -230,4 +262,5 @@ export interface BroService<MODEL extends object, API_QUERY = unknown, API_PAGE
230
262
  export interface BroServerPage<MODEL extends object> {
231
263
  rows: MODEL[];
232
264
  total: number;
233
- }
265
+ }
266
+ export type { TableBroSkinElements };