@table-bro/angular 0.0.0-stage → 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Table, Bro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,170 @@
1
- # Temporary Holding Version
1
+ # @table-bro/angular
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 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.
5
+
6
+ The wrapper does not depend on a skin. It works with any registered skin of Table, Bro: Angular Material
7
+ (`@table-bro/angular-material`), Bootstrap (`@table-bro/bootstrap`) or native (`@table-bro/native`).
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install @table-bro/core @table-bro/angular
13
+ ```
14
+
15
+ Add a skin: `@table-bro/angular-material` for Angular Material, or `@table-bro/bootstrap` or `@table-bro/native`. The
16
+ README of the skin gives its CSS and its registration, `provideTableBroMatSkin()` for Angular Material.
17
+
18
+ ## Use
19
+
20
+ ```ts
21
+ import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
22
+
23
+ import {TableBroNg} from '@table-bro/angular';
24
+ import type {BroCol, BroConfig, BroRow} from '@table-bro/core';
25
+
26
+ interface Order extends BroRow {
27
+ description: string;
28
+ price: number;
29
+ }
30
+
31
+ @Component({
32
+ selector: 'app-orders',
33
+ template: `<table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"></table-bro-ng>`,
34
+ changeDetection: ChangeDetectionStrategy.OnPush,
35
+ imports: [
36
+ TableBroNg
37
+ ]
38
+ })
39
+ export class OrdersComponent {
40
+ cols = signal<BroCol<Order>[]>([
41
+ {title: 'Description', name: 'description'},
42
+ {title: 'Price', name: 'price', dataType: 'number', className: 'bro-col_text-right'}
43
+ ]);
44
+
45
+ rows = signal<Order[]>([
46
+ {description: 'Modern Cotton Chair', price: 100},
47
+ {description: 'Awesome Concrete Ball', price: 200}
48
+ ]);
49
+
50
+ conf = signal<BroConfig>({skin: 'mat', toolbarTitle: 'Orders'});
51
+ }
52
+ ```
53
+
54
+ ### The server mode
55
+
56
+ With `server: true` the table sorts, filters and pages nothing itself: it gives every gesture of the user as a
57
+ `query`, and the page answers with the rows and the total. The page loads the first page itself:
58
+
59
+ ```ts
60
+ import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
61
+
62
+ import {TableBroNg} from '@table-bro/angular';
63
+ import type {BroCol, BroConfig, BroQuery, BroRow} from '@table-bro/core';
64
+
65
+ interface Order extends BroRow {
66
+ description: string;
67
+ price: number;
68
+ }
69
+
70
+ @Component({
71
+ selector: 'app-orders',
72
+ template: `
73
+ <table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"
74
+ [total]="total()" [rowsToAppend]="rowsToAppend()" [failed]="failed()"
75
+ (query)="onQueryTable($event)"></table-bro-ng>`,
76
+ changeDetection: ChangeDetectionStrategy.OnPush,
77
+ imports: [
78
+ TableBroNg
79
+ ]
80
+ })
81
+ export class OrdersComponent {
82
+ cols = signal<BroCol<Order>[]>([
83
+ {title: 'Description', name: 'description'},
84
+ {title: 'Price', name: 'price', dataType: 'number', className: 'bro-col_text-right'}
85
+ ]);
86
+
87
+ conf = signal<BroConfig>({skin: 'mat', server: true, pageSize: 10});
88
+
89
+ rows = signal<Order[] | undefined>(undefined);
90
+ rowsToAppend = signal<Order[] | undefined>(undefined);
91
+ total = signal<number>(0);
92
+ failed = signal<boolean>(false);
93
+
94
+ private lastQuery?: BroQuery<Order>;
95
+ private loadedQuery = '';
96
+
97
+ constructor() {
98
+ this.onQueryTable({page: 1, pageSize: 10});
99
+ }
100
+
101
+ async onQueryTable(query: BroQuery<Order>) {
102
+ const queryKey = JSON.stringify([query.filter, query.sort]);
103
+
104
+ this.lastQuery = query;
105
+
106
+ try {
107
+ const {rows, total} = await loadOrders(query); // your API
108
+
109
+ if (query !== this.lastQuery) {
110
+ return; // a newer query came while this one loaded
111
+ }
112
+
113
+ this.failed.set(false);
114
+ this.total.set(total);
115
+
116
+ // Other filters or another sort replace the rows, the next pages of the same query add to them
117
+ if (queryKey !== this.loadedQuery) {
118
+ this.loadedQuery = queryKey;
119
+ this.rows.set(rows);
120
+ } else {
121
+ this.rowsToAppend.set(rows);
122
+ }
123
+ } catch {
124
+ if (query === this.lastQuery) {
125
+ this.failed.set(true);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## Inputs
133
+
134
+ | Input | Meaning |
135
+ | --- | --- |
136
+ | `cols`, `conf` | Columns and config of the table. The element takes them only at `configure`, so a new value creates a new table. |
137
+ | `rows` | An array replaces the rows, `[]` shows an empty state. Without a value (`undefined` or `null`, as before the first value of an `async` pipe) the table keeps what it shows. |
138
+ | `loading` | `true` shows the loading state of a table without rows, or a refresh over the rows it shows. |
139
+ | `failed` | `true` shows the failed state. |
140
+ | `total` | The total number of rows on the server. It is read together with `rows` or `rowsToAppend`, as one delivery. |
141
+ | `rowsToAppend` | The rows of the next page or of a larger page size, for the last `query`. |
142
+ | `filter` | The filters of the page outside the table, for example of a filter drawer. The table takes a new value as a change of its own filters. |
143
+ | `emptyState` | The state for empty `rows`: `'not-found'` for an empty answer to a query with filters. |
144
+ | `tableId` | The `id` of the `<table-bro>` element. |
145
+ | `theme` | The theme of this table: `'light'`, `'dark'` or `'auto'`. |
146
+
147
+ ## Outputs
148
+
149
+ | Output | Meaning |
150
+ | --- | --- |
151
+ | `query` | The load intent of the server mode as a `BroQuery`: page, page size, filters and sort. One gesture gives one query. |
152
+ | `action` | Every other action of the `bro-action` event: custom cells, toolbar, paginator. |
153
+
154
+ ## Contracts
155
+
156
+ `BroFacade`, `BroServerFacade` describe the wrapper; `BroContainer`, `BroServerContainer` describe the page
157
+ component that feeds it. `BroService` and `BroServerPage` describe the service of the page that turns a `BroQuery`
158
+ into a request of your API and its answer into rows with a total.
159
+
160
+ ## Build
161
+
162
+ ```sh
163
+ npm run build:angular
164
+ ```
165
+
166
+ The root of the repository builds the package into `dist/table-bro.angular`.
167
+
168
+ ## License
169
+
170
+ MIT
@@ -0,0 +1,346 @@
1
+ import { isPlatformBrowser } from "@angular/common";
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";
5
+ var TableBroNg = class TableBroNg {
6
+ constructor() {
7
+ this.cols = input.required(...ngDevMode ? [{ debugName: "cols" }] : /* istanbul ignore next */ []);
8
+ this.rows = input.required(...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
9
+ this.conf = input.required(...ngDevMode ? [{ debugName: "conf" }] : /* istanbul ignore next */ []);
10
+ this.total = input(...ngDevMode ? [void 0, { debugName: "total" }] : /* istanbul ignore next */ []);
11
+ this.rowsToAppend = input(...ngDevMode ? [void 0, { debugName: "rowsToAppend" }] : /* istanbul ignore next */ []);
12
+ this.loading = input(false, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
13
+ this.failed = input(false, ...ngDevMode ? [{ debugName: "failed" }] : /* istanbul ignore next */ []);
14
+ this.filter = input(...ngDevMode ? [void 0, { debugName: "filter" }] : /* istanbul ignore next */ []);
15
+ this.emptyState = input(...ngDevMode ? [void 0, { debugName: "emptyState" }] : /* istanbul ignore next */ []);
16
+ this.tableId = input(...ngDevMode ? [void 0, { debugName: "tableId" }] : /* istanbul ignore next */ []);
17
+ this.theme = input(...ngDevMode ? [void 0, { debugName: "theme" }] : /* istanbul ignore next */ []);
18
+ this.action = output();
19
+ this.query = output();
20
+ this.appliedLoading = false;
21
+ this.appliedFailed = false;
22
+ this.elementRef = inject(ElementRef);
23
+ this.ngZone = inject(NgZone);
24
+ if (!isPlatformBrowser(inject(PLATFORM_ID))) return;
25
+ effect(() => {
26
+ const cols = this.cols();
27
+ const conf = this.conf();
28
+ const tableId = this.tableId();
29
+ this.ngZone.runOutsideAngular(() => untracked(() => this.createTableBro(cols, conf, tableId)));
30
+ });
31
+ effect(() => {
32
+ const rows = this.rows();
33
+ this.ngZone.runOutsideAngular(() => untracked(() => this.deliverRows(rows)));
34
+ });
35
+ effect(() => {
36
+ const rowsToAppend = this.rowsToAppend();
37
+ if (!rowsToAppend) return;
38
+ this.ngZone.runOutsideAngular(() => this.tableBro.appendRows(rowsToAppend, { total: untracked(this.total) }));
39
+ });
40
+ effect(() => {
41
+ const failed = this.failed();
42
+ const loading = this.loading();
43
+ this.ngZone.runOutsideAngular(() => untracked(() => this.applyState(failed, loading)));
44
+ });
45
+ effect(() => {
46
+ const filter = this.filter();
47
+ this.ngZone.runOutsideAngular(() => this.applyFilter(filter));
48
+ });
49
+ effect(() => {
50
+ const theme = this.theme();
51
+ if (theme !== void 0) this.ngZone.runOutsideAngular(() => this.tableBro.theme = theme);
52
+ });
53
+ }
54
+ createTableBro(cols, conf, tableId) {
55
+ const rows = this.rows();
56
+ const theme = this.theme();
57
+ this.tableBro = document.createElement("table-bro");
58
+ this.appliedLoading = false;
59
+ this.appliedFailed = false;
60
+ if (tableId) this.tableBro.setAttribute("id", tableId);
61
+ if (theme !== void 0) this.tableBro.theme = theme;
62
+ this.deliveredRows = !conf.server && rows?.length ? rows : void 0;
63
+ this.tableBro.configure({
64
+ cols,
65
+ rows: this.deliveredRows,
66
+ conf
67
+ });
68
+ this.tableBro.addEventListener(events.actionEvent, (event) => {
69
+ if (event.target.closest("table-bro") === this.tableBro) this.tableBroListener(event);
70
+ });
71
+ this.elementRef.nativeElement.replaceChildren(this.tableBro);
72
+ this.appliedFilter = void 0;
73
+ this.applyFilter(this.filter());
74
+ this.deliverRows(rows);
75
+ if (this.failed()) this.fail();
76
+ else this.applyLoading(this.loading());
77
+ }
78
+ applyFilter(filter) {
79
+ if (!filter || filter === this.appliedFilter) return;
80
+ this.appliedFilter = filter;
81
+ dispatchAction(this.tableBro, events.filtersFilterEvent, filter);
82
+ }
83
+ deliverRows(rows) {
84
+ if (!rows || rows === this.deliveredRows) return;
85
+ this.deliveredRows = rows;
86
+ this.tableBro.replaceRows(rows, {
87
+ total: this.total(),
88
+ emptyState: this.emptyState()
89
+ });
90
+ }
91
+ applyLoading(loading) {
92
+ if (loading && this.rows()?.length) this.tableBro.setState("refreshing");
93
+ else if (loading) {
94
+ this.tableBro.setState("loading");
95
+ this.deliveredRows = void 0;
96
+ } else if (this.appliedLoading) this.tableBro.setState("idle");
97
+ this.appliedLoading = loading;
98
+ }
99
+ fail() {
100
+ this.tableBro.setState("fail");
101
+ this.appliedFailed = true;
102
+ this.deliveredRows = void 0;
103
+ }
104
+ applyState(failed, loading) {
105
+ if (failed) {
106
+ if (!this.appliedFailed) this.fail();
107
+ return;
108
+ }
109
+ if (this.appliedFailed) {
110
+ this.appliedFailed = false;
111
+ this.deliverRows(this.rows());
112
+ }
113
+ this.applyLoading(loading);
114
+ }
115
+ tableBroListener(event) {
116
+ const detail = event.detail;
117
+ if (detail.action === events.filtersServerLoadedEvent) {
118
+ const query = detail.data;
119
+ this.ngZone.run(() => this.query.emit(query));
120
+ } else this.ngZone.run(() => this.action.emit(detail));
121
+ }
122
+ static {
123
+ this.ɵfac = i0.ɵɵngDeclareFactory({
124
+ minVersion: "12.0.0",
125
+ version: "22.2.2",
126
+ ngImport: i0,
127
+ type: TableBroNg,
128
+ deps: [],
129
+ target: i0.ɵɵFactoryTarget.Component
130
+ });
131
+ }
132
+ static {
133
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
134
+ minVersion: "17.1.0",
135
+ version: "22.2.2",
136
+ type: TableBroNg,
137
+ isStandalone: true,
138
+ selector: "table-bro-ng",
139
+ inputs: {
140
+ cols: {
141
+ classPropertyName: "cols",
142
+ publicName: "cols",
143
+ isSignal: true,
144
+ isRequired: true,
145
+ transformFunction: null
146
+ },
147
+ rows: {
148
+ classPropertyName: "rows",
149
+ publicName: "rows",
150
+ isSignal: true,
151
+ isRequired: true,
152
+ transformFunction: null
153
+ },
154
+ conf: {
155
+ classPropertyName: "conf",
156
+ publicName: "conf",
157
+ isSignal: true,
158
+ isRequired: true,
159
+ transformFunction: null
160
+ },
161
+ total: {
162
+ classPropertyName: "total",
163
+ publicName: "total",
164
+ isSignal: true,
165
+ isRequired: false,
166
+ transformFunction: null
167
+ },
168
+ rowsToAppend: {
169
+ classPropertyName: "rowsToAppend",
170
+ publicName: "rowsToAppend",
171
+ isSignal: true,
172
+ isRequired: false,
173
+ transformFunction: null
174
+ },
175
+ loading: {
176
+ classPropertyName: "loading",
177
+ publicName: "loading",
178
+ isSignal: true,
179
+ isRequired: false,
180
+ transformFunction: null
181
+ },
182
+ failed: {
183
+ classPropertyName: "failed",
184
+ publicName: "failed",
185
+ isSignal: true,
186
+ isRequired: false,
187
+ transformFunction: null
188
+ },
189
+ filter: {
190
+ classPropertyName: "filter",
191
+ publicName: "filter",
192
+ isSignal: true,
193
+ isRequired: false,
194
+ transformFunction: null
195
+ },
196
+ emptyState: {
197
+ classPropertyName: "emptyState",
198
+ publicName: "emptyState",
199
+ isSignal: true,
200
+ isRequired: false,
201
+ transformFunction: null
202
+ },
203
+ tableId: {
204
+ classPropertyName: "tableId",
205
+ publicName: "tableId",
206
+ isSignal: true,
207
+ isRequired: false,
208
+ transformFunction: null
209
+ },
210
+ theme: {
211
+ classPropertyName: "theme",
212
+ publicName: "theme",
213
+ isSignal: true,
214
+ isRequired: false,
215
+ transformFunction: null
216
+ }
217
+ },
218
+ outputs: {
219
+ action: "action",
220
+ query: "query"
221
+ },
222
+ ngImport: i0,
223
+ template: "",
224
+ isInline: true,
225
+ styles: [":host{display:block}\n"],
226
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
227
+ });
228
+ }
229
+ };
230
+ i0.ɵɵngDeclareClassMetadata({
231
+ minVersion: "12.0.0",
232
+ version: "22.2.2",
233
+ ngImport: i0,
234
+ type: TableBroNg,
235
+ decorators: [{
236
+ type: Component,
237
+ args: [{
238
+ selector: "table-bro-ng",
239
+ template: "",
240
+ changeDetection: ChangeDetectionStrategy.OnPush,
241
+ styles: [":host{display:block}\n"]
242
+ }]
243
+ }],
244
+ ctorParameters: () => [],
245
+ propDecorators: {
246
+ cols: [{
247
+ type: i0.Input,
248
+ args: [{
249
+ isSignal: true,
250
+ alias: "cols",
251
+ required: true
252
+ }]
253
+ }],
254
+ rows: [{
255
+ type: i0.Input,
256
+ args: [{
257
+ isSignal: true,
258
+ alias: "rows",
259
+ required: true
260
+ }]
261
+ }],
262
+ conf: [{
263
+ type: i0.Input,
264
+ args: [{
265
+ isSignal: true,
266
+ alias: "conf",
267
+ required: true
268
+ }]
269
+ }],
270
+ total: [{
271
+ type: i0.Input,
272
+ args: [{
273
+ isSignal: true,
274
+ alias: "total",
275
+ required: false
276
+ }]
277
+ }],
278
+ rowsToAppend: [{
279
+ type: i0.Input,
280
+ args: [{
281
+ isSignal: true,
282
+ alias: "rowsToAppend",
283
+ required: false
284
+ }]
285
+ }],
286
+ loading: [{
287
+ type: i0.Input,
288
+ args: [{
289
+ isSignal: true,
290
+ alias: "loading",
291
+ required: false
292
+ }]
293
+ }],
294
+ failed: [{
295
+ type: i0.Input,
296
+ args: [{
297
+ isSignal: true,
298
+ alias: "failed",
299
+ required: false
300
+ }]
301
+ }],
302
+ filter: [{
303
+ type: i0.Input,
304
+ args: [{
305
+ isSignal: true,
306
+ alias: "filter",
307
+ required: false
308
+ }]
309
+ }],
310
+ emptyState: [{
311
+ type: i0.Input,
312
+ args: [{
313
+ isSignal: true,
314
+ alias: "emptyState",
315
+ required: false
316
+ }]
317
+ }],
318
+ tableId: [{
319
+ type: i0.Input,
320
+ args: [{
321
+ isSignal: true,
322
+ alias: "tableId",
323
+ required: false
324
+ }]
325
+ }],
326
+ theme: [{
327
+ type: i0.Input,
328
+ args: [{
329
+ isSignal: true,
330
+ alias: "theme",
331
+ required: false
332
+ }]
333
+ }],
334
+ action: [{
335
+ type: i0.Output,
336
+ args: ["action"]
337
+ }],
338
+ query: [{
339
+ type: i0.Output,
340
+ args: ["query"]
341
+ }]
342
+ }
343
+ });
344
+ export { TableBroNg };
345
+
346
+ //# sourceMappingURL=table-bro-angular.mjs.map
@@ -0,0 +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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"}
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@table-bro/angular",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.1.0",
4
+ "description": "Pixel-perfect Material data tables for Angular, and more. A Web Components table engine on top of existing UI components.",
5
+ "keywords": [
6
+ "angular",
7
+ "table",
8
+ "data-table",
9
+ "datatable",
10
+ "data-grid",
11
+ "web-components"
12
+ ],
13
+ "author": "table-bro",
14
+ "homepage": "https://table-bro.com",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/table-bro/table-bro.git",
18
+ "directory": "packages/angular"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/table-bro/table-bro/issues"
22
+ },
23
+ "license": "MIT",
24
+ "sideEffects": false,
25
+ "peerDependencies": {
26
+ "@angular/common": ">=20 <23",
27
+ "@angular/core": ">=20 <23",
28
+ "@table-bro/core": "^1.1.0"
29
+ },
30
+ "dependencies": {
31
+ "tslib": "^2.3.0"
32
+ },
33
+ "module": "fesm2022/table-bro-angular.mjs",
34
+ "typings": "types/table-bro-angular.d.ts",
35
+ "exports": {
36
+ "./package.json": {
37
+ "default": "./package.json"
38
+ },
39
+ ".": {
40
+ "types": "./types/table-bro-angular.d.ts",
41
+ "default": "./fesm2022/table-bro-angular.mjs"
42
+ }
43
+ },
44
+ "type": "module"
6
45
  }
@@ -0,0 +1,233 @@
1
+ import { BroActionDetail, BroCol, BroConfig, BroEmptyState, BroFilters, BroQuery, BroUserTheme } from "@table-bro/core";
2
+ import * as i0 from "@angular/core";
3
+ import { InputSignal, OutputEmitterRef, Signal } from "@angular/core";
4
+ /**
5
+ * Public facade contract for a table component.
6
+ *
7
+ * Defines the public input surface exposed to consumers
8
+ * (parent components and templates).
9
+ *
10
+ * All properties are Angular input signals managed by the framework and
11
+ * must be treated as immutable from the consumer perspective.
12
+ *
13
+ * @typeParam MODEL Row model displayed by the table
14
+ */
15
+ export interface BroFacade<MODEL extends object> {
16
+ /** Column definitions and UI configuration */
17
+ cols: InputSignal<BroCol<MODEL>[]>;
18
+ /** Table rows; without a value the table keeps what it shows */
19
+ rows: InputSignal<MODEL[] | undefined | null>;
20
+ /** Table configuration (page size, title, etc.) */
21
+ conf: InputSignal<BroConfig<MODEL>>;
22
+ /** The loading of the rows: the loading state, or a refresh over the shown rows */
23
+ loading?: InputSignal<boolean>;
24
+ /** A failed loading of the rows: the fail state */
25
+ failed?: InputSignal<boolean>;
26
+ action?: OutputEmitterRef<BroActionDetail<MODEL>>;
27
+ }
28
+ /**
29
+ * Public facade contract for a server-driven table.
30
+ *
31
+ * Extends BroFacade with server-specific responsibilities such as
32
+ * total row count exposure and query emission.
33
+ *
34
+ * Intended to be injected into page components that handle
35
+ * server-side data fetching.
36
+ *
37
+ * @typeParam MODEL Row model displayed by the table
38
+ */
39
+ export interface BroServerFacade<MODEL extends object> extends BroFacade<MODEL> {
40
+ /** Total number of rows available on the server */
41
+ total: InputSignal<number | undefined>;
42
+ rowsToAppend?: InputSignal<MODEL[] | undefined>;
43
+ /**
44
+ * Emits table queries whenever pagination, sorting or filtering changes.
45
+ * Handled by a page-level server container.
46
+ */
47
+ query: OutputEmitterRef<BroQuery<MODEL>>;
48
+ }
49
+ /**
50
+ * Page-level table state contract.
51
+ *
52
+ * Describes the local state and behavior of a page component that injects
53
+ * and coordinates a BroFacade.
54
+ *
55
+ * The container:
56
+ * - adapts facade data for the page template
57
+ * - owns page-specific reactive state
58
+ * - orchestrates table initialization and lifecycle
59
+ *
60
+ * All properties are writable signals owned by the page component.
61
+ *
62
+ * @typeParam MODEL Row model displayed by the table
63
+ */
64
+ export interface BroContainer<MODEL extends object> {
65
+ /** Reactive column definitions bound to the table */
66
+ cols: Signal<BroCol<MODEL>[]>;
67
+ /** Reactive row data consumed by the template */
68
+ rows: Signal<MODEL[] | undefined | null>;
69
+ /** Reactive table configuration for the page */
70
+ conf: Signal<BroConfig<MODEL>>;
71
+ /** Page-level initialization hook coordinating the facade */
72
+ initTable: () => void;
73
+ }
74
+ /**
75
+ * Page-level state contract for a server-driven table.
76
+ *
77
+ * Extends BroContainer with server-specific state and query handling logic.
78
+ *
79
+ * Implementations are responsible for:
80
+ * - handling table queries
81
+ * - calling backend services
82
+ * - updating rows and total count
83
+ *
84
+ * @typeParam MODEL Row model displayed by the table
85
+ * @typeParam EXTRA Additional backend-specific filter fields
86
+ */
87
+ export interface BroServerContainer<MODEL extends object, EXTRA extends object = object> extends BroContainer<MODEL> {
88
+ /** Reactive total row count provided by the backend */
89
+ total: Signal<number>;
90
+ rowsToAppend?: Signal<MODEL[] | undefined>;
91
+ /**
92
+ * Handles table queries emitted by the facade.
93
+ * Triggers server communication and state updates.
94
+ */
95
+ onQueryTable: (query: BroQuery<MODEL, EXTRA>) => void;
96
+ }
97
+ /**
98
+ * The Angular wrapper of <table-bro>: it translates the inputs into the methods of the element and the bro-action event
99
+ * into the outputs. The table stays one: the wrapper renders nothing of its own and keeps no pagination, sort or filters.
100
+ * The table works outside the Angular zone, so its animation frames start no change detection; the outputs emit inside it.
101
+ */
102
+ export declare class TableBroNg<MODEL extends object> implements BroServerFacade<MODEL> {
103
+ cols: import("@angular/core").InputSignal<BroCol<MODEL>[]>;
104
+ /** The rows of the table; without a value, as before the first value of an async pipe, the table keeps what it shows. */
105
+ rows: import("@angular/core").InputSignal<MODEL[] | null | undefined>;
106
+ conf: import("@angular/core").InputSignal<BroConfig<MODEL>>;
107
+ total: import("@angular/core").InputSignal<number | undefined>;
108
+ rowsToAppend: import("@angular/core").InputSignal<MODEL[] | undefined>;
109
+ /** The loading of the rows: the loading state of a table without rows, a refresh over the rows that the table shows. */
110
+ loading: import("@angular/core").InputSignal<boolean>;
111
+ /** A failed loading of the rows: the fail state of the table. */
112
+ failed: import("@angular/core").InputSignal<boolean>;
113
+ /**
114
+ * The filters of the page outside the table, for example of a filter drawer. The table takes a new value as a change
115
+ * of its own filters: it filters its rows in the local mode and sends a query with them in the server mode.
116
+ * A cleared filter comes as undefined, null or an empty string; false and 0 are values of a filter.
117
+ */
118
+ filter: import("@angular/core").InputSignal<BroFilters<MODEL> | undefined>;
119
+ /** The state of the table for empty rows: 'not-found' for an empty answer to a query with filters; 'no-data' without it. */
120
+ emptyState: import("@angular/core").InputSignal<BroEmptyState | undefined>;
121
+ /** The id of the <table-bro> element; without it the table gives itself a unique one. */
122
+ tableId: import("@angular/core").InputSignal<string | undefined>;
123
+ /** The theme of this table; without it the table takes the theme from its config. */
124
+ theme: import("@angular/core").InputSignal<BroUserTheme | null | undefined>;
125
+ action: import("@angular/core").OutputEmitterRef<BroActionDetail<MODEL>>;
126
+ query: import("@angular/core").OutputEmitterRef<BroQuery<MODEL, object>>;
127
+ /** The loading that the table has got from the wrapper: its end without new rows ends the refresh. */
128
+ private appliedLoading;
129
+ /** The failure that the table shows: its end shows the rows of the page again. */
130
+ private appliedFailed;
131
+ /** The filters of the page the table has got from the wrapper: a new table gets the current ones again. */
132
+ private appliedFilter;
133
+ /** The rows the table has got from the wrapper: the same array is not delivered twice. */
134
+ private deliveredRows;
135
+ private tableBro;
136
+ private elementRef;
137
+ private ngZone;
138
+ constructor();
139
+ private createTableBro;
140
+ private applyFilter;
141
+ private deliverRows;
142
+ /** A loading over the rows that the page still gives is a refresh; without rows it is the loading state. */
143
+ private applyLoading;
144
+ private fail;
145
+ /**
146
+ * The failure, else the loading. The end of a failure shows the rows of the page again, also the same array, which no
147
+ * new value of the rows brings; a loading that goes on keeps its refresh over them.
148
+ */
149
+ private applyState;
150
+ private tableBroListener;
151
+ static ɵfac: i0.ɵɵFactoryDeclaration<TableBroNg<any>, never>;
152
+ static ɵcmp: i0.ɵɵComponentDeclaration<TableBroNg<any>, "table-bro-ng", never, {
153
+ "cols": {
154
+ "alias": "cols";
155
+ "required": true;
156
+ "isSignal": true;
157
+ };
158
+ "rows": {
159
+ "alias": "rows";
160
+ "required": true;
161
+ "isSignal": true;
162
+ };
163
+ "conf": {
164
+ "alias": "conf";
165
+ "required": true;
166
+ "isSignal": true;
167
+ };
168
+ "total": {
169
+ "alias": "total";
170
+ "required": false;
171
+ "isSignal": true;
172
+ };
173
+ "rowsToAppend": {
174
+ "alias": "rowsToAppend";
175
+ "required": false;
176
+ "isSignal": true;
177
+ };
178
+ "loading": {
179
+ "alias": "loading";
180
+ "required": false;
181
+ "isSignal": true;
182
+ };
183
+ "failed": {
184
+ "alias": "failed";
185
+ "required": false;
186
+ "isSignal": true;
187
+ };
188
+ "filter": {
189
+ "alias": "filter";
190
+ "required": false;
191
+ "isSignal": true;
192
+ };
193
+ "emptyState": {
194
+ "alias": "emptyState";
195
+ "required": false;
196
+ "isSignal": true;
197
+ };
198
+ "tableId": {
199
+ "alias": "tableId";
200
+ "required": false;
201
+ "isSignal": true;
202
+ };
203
+ "theme": {
204
+ "alias": "theme";
205
+ "required": false;
206
+ "isSignal": true;
207
+ };
208
+ }, {
209
+ "action": "action";
210
+ "query": "query";
211
+ }, never, never, true, never>;
212
+ }
213
+ /**
214
+ * Service contract responsible for translating between the table domain model
215
+ * and a specific backend API.
216
+ *
217
+ * BroService defines how:
218
+ * - a generic BroQuery (pagination, sorting, filtering) is transformed into
219
+ * an API-specific request format
220
+ * - an API-specific response is mapped back into a normalized BroServerPage
221
+ *
222
+ * This abstraction allows the table component and its containers to remain
223
+ * backend-agnostic, while each integration provides its own mapping logic.
224
+ *
225
+ */
226
+ export interface BroService<MODEL extends object, API_QUERY = unknown, API_PAGE = unknown, EXTRA extends object = object> {
227
+ mapServerQuery: (query: BroQuery<MODEL, EXTRA>) => API_QUERY;
228
+ mapServerResponse: (response: API_PAGE) => BroServerPage<MODEL>;
229
+ }
230
+ export interface BroServerPage<MODEL extends object> {
231
+ rows: MODEL[];
232
+ total: number;
233
+ }