@table-bro/mcp 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 +21 -0
- package/README.md +89 -2
- package/dist/docs/docs.d.ts +21 -0
- package/dist/docs/docs.js +103 -0
- package/dist/docs/pages.d.ts +25 -0
- package/dist/docs/pages.js +1404 -0
- package/dist/docs/topics.d.ts +11 -0
- package/dist/docs/topics.js +396 -0
- package/dist/extension.d.ts +23 -0
- package/dist/extension.js +1 -0
- package/dist/generate/angular.d.ts +26 -0
- package/dist/generate/angular.js +241 -0
- package/dist/generate/cells.d.ts +22 -0
- package/dist/generate/cells.js +157 -0
- package/dist/generate/code.d.ts +48 -0
- package/dist/generate/code.js +153 -0
- package/dist/generate/config.d.ts +16 -0
- package/dist/generate/config.js +95 -0
- package/dist/generate/fillings.d.ts +49 -0
- package/dist/generate/fillings.js +130 -0
- package/dist/generate/filters.d.ts +36 -0
- package/dist/generate/filters.js +254 -0
- package/dist/generate/generate-code.d.ts +34 -0
- package/dist/generate/generate-code.js +59 -0
- package/dist/generate/names.d.ts +52 -0
- package/dist/generate/names.js +93 -0
- package/dist/generate/rows.d.ts +5 -0
- package/dist/generate/rows.js +22 -0
- package/dist/generate/scaffold.d.ts +35 -0
- package/dist/generate/scaffold.js +62 -0
- package/dist/generate/service.d.ts +16 -0
- package/dist/generate/service.js +320 -0
- package/dist/generate/setup.d.ts +34 -0
- package/dist/generate/setup.js +156 -0
- package/dist/generate/vanilla.d.ts +10 -0
- package/dist/generate/vanilla.js +160 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +10 -0
- package/dist/project/inspect-project.d.ts +53 -0
- package/dist/project/inspect-project.js +223 -0
- package/dist/server/main.d.ts +2 -0
- package/dist/server/main.js +8 -0
- package/dist/server/server.d.ts +17 -0
- package/dist/server/server.js +212 -0
- package/dist/spec/builtin-specs.d.ts +13 -0
- package/dist/spec/builtin-specs.js +167 -0
- package/dist/spec/css-variables.d.ts +22 -0
- package/dist/spec/css-variables.js +54 -0
- package/dist/spec/schema.d.ts +19 -0
- package/dist/spec/schema.js +278 -0
- package/dist/spec/skin-config.d.ts +30 -0
- package/dist/spec/skin-config.js +67 -0
- package/dist/spec/spec.d.ts +157 -0
- package/dist/spec/spec.js +2 -0
- package/dist/spec/validate-spec.d.ts +54 -0
- package/dist/spec/validate-spec.js +979 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +57 -4
|
@@ -0,0 +1,1404 @@
|
|
|
1
|
+
import { setupSteps } from '../generate/setup.js';
|
|
2
|
+
/** The languages of the files of code by the languages of the steps of the setup. */
|
|
3
|
+
const setupLanguages = { bash: 'bash', html: 'html', json: 'json', scss: 'scss', typescript: 'ts' };
|
|
4
|
+
/**
|
|
5
|
+
* The setup of a project of one skin, as get_started of the MCP gives it: its steps as an ordered list of the page and
|
|
6
|
+
* their code as the files of code of the page.
|
|
7
|
+
* @param target - an Angular project or Vanilla JS
|
|
8
|
+
* @param skin - the skin of the project
|
|
9
|
+
*/
|
|
10
|
+
function setup(target, skin) {
|
|
11
|
+
const files = [];
|
|
12
|
+
const text = setupSteps(target, skin).map((step, i) => {
|
|
13
|
+
const blocks = [`${i + 1}. **${step.title}**`];
|
|
14
|
+
if (step.text) {
|
|
15
|
+
blocks.push(` ${step.text}`);
|
|
16
|
+
}
|
|
17
|
+
if (step.code) {
|
|
18
|
+
const language = setupLanguages[step.language ?? 'text'] ?? 'text';
|
|
19
|
+
const file = `setup-${skin}-${i + 1}.${language}`;
|
|
20
|
+
files.push({ file, language, code: `${step.code}\n` });
|
|
21
|
+
blocks.push(` {{${file}}}`);
|
|
22
|
+
}
|
|
23
|
+
return blocks.join('\n\n');
|
|
24
|
+
}).join('\n\n');
|
|
25
|
+
return { text, files };
|
|
26
|
+
}
|
|
27
|
+
const setupMat = setup('angular', 'mat');
|
|
28
|
+
const setupBs = setup('vanilla', 'bs');
|
|
29
|
+
const setupNv = setup('vanilla', 'nv');
|
|
30
|
+
/** The pages of Docs, in the order of reading. Island Details have their own pages on the site, apart from this package. */
|
|
31
|
+
export const docPages = [
|
|
32
|
+
{
|
|
33
|
+
path: 'getting-started',
|
|
34
|
+
name: 'Getting started',
|
|
35
|
+
text: `<!-- steps -->
|
|
36
|
+
Table, Bro is the Custom Element \`‹table-bro›\`: it takes the columns, the rows and a config with the name of its skin, and renders the table with its toolbar and its paginator.
|
|
37
|
+
|
|
38
|
+
### Angular
|
|
39
|
+
|
|
40
|
+
{{orders.component.ts}}
|
|
41
|
+
|
|
42
|
+
### Vanilla JS
|
|
43
|
+
|
|
44
|
+
{{orders.ts}}
|
|
45
|
+
|
|
46
|
+
<!-- setup -->
|
|
47
|
+
### Table, Bro + Angular Material
|
|
48
|
+
|
|
49
|
+
[See a working page](https://table-bro.com/samples/angular-material)
|
|
50
|
+
|
|
51
|
+
${setupMat.text}
|
|
52
|
+
|
|
53
|
+
### Table, Bro + Vanilla Bootstrap
|
|
54
|
+
|
|
55
|
+
[See a working page](https://table-bro.com/samples/bootstrap)
|
|
56
|
+
|
|
57
|
+
${setupBs.text}
|
|
58
|
+
|
|
59
|
+
### Table, Bro + Vanilla HTML
|
|
60
|
+
|
|
61
|
+
[See a working page](https://table-bro.com/samples/native)
|
|
62
|
+
|
|
63
|
+
${setupNv.text}`,
|
|
64
|
+
files: [
|
|
65
|
+
{ file: 'orders.component.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
|
|
66
|
+
|
|
67
|
+
import {TableBroNg} from '@table-bro/angular';
|
|
68
|
+
import type {BroCol, BroConfig, BroRow} from '@table-bro/core';
|
|
69
|
+
|
|
70
|
+
interface Order extends BroRow {
|
|
71
|
+
description: string;
|
|
72
|
+
price: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Component({
|
|
76
|
+
selector: 'app-orders',
|
|
77
|
+
template: \`
|
|
78
|
+
<table-bro-ng class="bro-table-shadow-container" tableId="orders"
|
|
79
|
+
[cols]="cols()" [rows]="rows()" [conf]="conf()"></table-bro-ng>\`,
|
|
80
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
81
|
+
imports: [
|
|
82
|
+
TableBroNg
|
|
83
|
+
]
|
|
84
|
+
})
|
|
85
|
+
export class OrdersComponent {
|
|
86
|
+
cols = signal<BroCol<Order>[]>([
|
|
87
|
+
{
|
|
88
|
+
title: 'Description',
|
|
89
|
+
name: 'description'
|
|
90
|
+
}, {
|
|
91
|
+
title: 'Price',
|
|
92
|
+
name: 'price',
|
|
93
|
+
dataType: 'number',
|
|
94
|
+
className: 'bro-col_text-right'
|
|
95
|
+
}
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
rows = signal<Order[]>([
|
|
99
|
+
{
|
|
100
|
+
description: 'Modern Cotton Chair',
|
|
101
|
+
price: 100
|
|
102
|
+
}, {
|
|
103
|
+
description: 'Awesome Concrete Ball',
|
|
104
|
+
price: 200
|
|
105
|
+
}
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
conf = signal<BroConfig>({
|
|
109
|
+
skin: 'mat',
|
|
110
|
+
toolbarTitle: 'Orders'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
` },
|
|
114
|
+
{ file: 'orders.ts', language: 'ts', code: `import type {BroCol, BroConfig, BroRow} from '@table-bro/core';
|
|
115
|
+
|
|
116
|
+
interface Order extends BroRow {
|
|
117
|
+
description: string;
|
|
118
|
+
price: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cols: BroCol<Order>[] = [
|
|
122
|
+
{
|
|
123
|
+
title: 'Description',
|
|
124
|
+
name: 'description'
|
|
125
|
+
}, {
|
|
126
|
+
title: 'Price',
|
|
127
|
+
name: 'price',
|
|
128
|
+
dataType: 'number',
|
|
129
|
+
className: 'bro-col_text-right'
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const rows: Order[] = [
|
|
134
|
+
{
|
|
135
|
+
description: 'Modern Cotton Chair',
|
|
136
|
+
price: 100
|
|
137
|
+
}, {
|
|
138
|
+
description: 'Awesome Concrete Ball',
|
|
139
|
+
price: 200
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
const conf: BroConfig = {
|
|
144
|
+
skin: 'bs', // 'nv' for Vanilla HTML
|
|
145
|
+
toolbarTitle: 'Orders'
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const table = document.createElement('table-bro');
|
|
149
|
+
|
|
150
|
+
table.id = 'orders';
|
|
151
|
+
table.configure({cols, rows, conf});
|
|
152
|
+
|
|
153
|
+
// The shadows of the table fall into the margins of this element: the visible table takes its full width
|
|
154
|
+
const container = document.createElement('div');
|
|
155
|
+
|
|
156
|
+
container.className = 'bro-table-shadow-container';
|
|
157
|
+
container.append(table);
|
|
158
|
+
document.body.append(container);
|
|
159
|
+
` },
|
|
160
|
+
...setupMat.files,
|
|
161
|
+
...setupBs.files,
|
|
162
|
+
...setupNv.files
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
path: 'basics/fixed-row-height',
|
|
167
|
+
name: 'Fixed row height',
|
|
168
|
+
text: `<!-- intro -->
|
|
169
|
+
All body rows of a table have one fixed height, and its header has one too. Give them with the CSS variables of the skin on the table itself, before it renders: the CSS and the script of the table take them together, without a new build of the skin.
|
|
170
|
+
|
|
171
|
+
{{fixed-row-height.css}}
|
|
172
|
+
|
|
173
|
+
#### From the SCSS sources
|
|
174
|
+
|
|
175
|
+
The package of each skin ships its built CSS and its SCSS sources. A build of the SCSS of the skin gives it other heights for all its tables: configure the skin where your SCSS loads it, with \`node_modules/@table-bro/<skin>/styles\` and \`node_modules/@table-bro/core/styles/mappings/production\` among the load paths of Sass.
|
|
176
|
+
|
|
177
|
+
{{fixed-row-height.scss}}`,
|
|
178
|
+
files: [
|
|
179
|
+
{ file: 'fixed-row-height.css', language: 'css', code: `#orders {
|
|
180
|
+
--bro-header-height: 64px;
|
|
181
|
+
--bro-row-height: 64px;
|
|
182
|
+
}
|
|
183
|
+
` },
|
|
184
|
+
{ file: 'fixed-row-height.scss', language: 'scss', code: `// The skin of the project: "table-bro.material", "table-bro.bootstrap" or "table-bro.native"
|
|
185
|
+
@use "table-bro.material" with ($bro-table-header-height: 64px, $bro-table-row-height: 64px);
|
|
186
|
+
` }
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
path: 'basics/fixed-column-width',
|
|
191
|
+
name: 'Fixed column width',
|
|
192
|
+
text: `<!-- intro -->
|
|
193
|
+
The table gives every column its optimal width by itself: \`autoColumnWidth\`, on by default, measures the titles and the values of the columns and shares the width of the table between them.
|
|
194
|
+
|
|
195
|
+
Any column can still have a fixed width. Give the column a class with \`className\` and the width of its cells in your CSS: the table measures the column at this width and keeps it, the other columns share the rest, and a longer value ends with an ellipsis and shows whole in the tooltip.
|
|
196
|
+
|
|
197
|
+
{{fixed-col-width.ts}}
|
|
198
|
+
|
|
199
|
+
{{fixed-col-width.css}}
|
|
200
|
+
|
|
201
|
+
A build from the SCSS sources has the mixin \`bro-text-ellipsis-width\` of the module \`table-bro.api\` for it:
|
|
202
|
+
|
|
203
|
+
{{fixed-col-width.scss}}`,
|
|
204
|
+
files: [
|
|
205
|
+
{ file: 'fixed-col-width.ts', language: 'ts', code: `import type {BroCol, BroRow} from '@table-bro/core';
|
|
206
|
+
|
|
207
|
+
interface Order extends BroRow {
|
|
208
|
+
description: string;
|
|
209
|
+
price: number;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const cols: BroCol<Order>[] = [
|
|
213
|
+
{
|
|
214
|
+
title: 'Description',
|
|
215
|
+
name: 'description',
|
|
216
|
+
className: 'bro-order_description'
|
|
217
|
+
}, {
|
|
218
|
+
title: 'Price',
|
|
219
|
+
name: 'price',
|
|
220
|
+
dataType: 'number',
|
|
221
|
+
className: 'bro-col_text-right'
|
|
222
|
+
}
|
|
223
|
+
];
|
|
224
|
+
` },
|
|
225
|
+
{ file: 'fixed-col-width.css', language: 'css', code: `.bro-order_description .bro-cell_component {
|
|
226
|
+
width: 400px;
|
|
227
|
+
}
|
|
228
|
+
` },
|
|
229
|
+
{ file: 'fixed-col-width.scss', language: 'scss', code: `@use "table-bro.api" as *;
|
|
230
|
+
|
|
231
|
+
@include bro-text-ellipsis-width('.bro-order_description', 400px);
|
|
232
|
+
` }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
path: 'basics/auto-detected-column-width',
|
|
237
|
+
name: 'Auto-detected column width',
|
|
238
|
+
text: `<!-- table-basics_auto-detected-column-width -->
|
|
239
|
+
Before its rows come, the table measures the titles of the columns: the header stands at their widths. The rows that come give the columns the widths of their values, and the header moves to them.
|
|
240
|
+
|
|
241
|
+
<!-- auto-column-width -->
|
|
242
|
+
#### Column widths without auto-detection
|
|
243
|
+
|
|
244
|
+
The auto-detection measures the values of a column and gives it the width of the longest one. An application that knows the widths it needs turns it off with \`autoColumnWidth: false\` in the config: the table measures nothing and writes no widths, and your CSS gives every column its width through its class, \`className\` of the column. Give the width with the mixin \`bro-text-ellipsis-width\` of the module \`table-bro.api\`, and the column is as wide as you need: a long value keeps to the one line of its row and ends with an ellipsis.
|
|
245
|
+
|
|
246
|
+
{{auto-column-width.ts}}
|
|
247
|
+
|
|
248
|
+
{{auto-column-width.scss}}
|
|
249
|
+
|
|
250
|
+
A table with a Filter Drawer keeps the auto-detection, \`autoColumnWidth: true\` by default. The drawer declares the width that it leaves the table: the columns take it at once and move with the drawer, and a value that does not fit any more ends with an ellipsis and shows whole in the tooltip. With \`autoColumnWidth: false\` the table follows neither the drawer nor a resize: the widths of your CSS stay as they are.`,
|
|
251
|
+
files: [
|
|
252
|
+
{ file: 'auto-column-width.ts', language: 'ts', code: `import type {BroCol, BroConfig, BroRow} from '@table-bro/core';
|
|
253
|
+
|
|
254
|
+
interface Order extends BroRow {
|
|
255
|
+
description: string;
|
|
256
|
+
price: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const conf: BroConfig = {
|
|
260
|
+
autoColumnWidth: false
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const cols: BroCol<Order>[] = [
|
|
264
|
+
{
|
|
265
|
+
title: 'Description',
|
|
266
|
+
name: 'description',
|
|
267
|
+
className: 'bro-order_description'
|
|
268
|
+
}, {
|
|
269
|
+
title: 'Price',
|
|
270
|
+
name: 'price',
|
|
271
|
+
dataType: 'number',
|
|
272
|
+
className: 'bro-order_price bro-col_text-right'
|
|
273
|
+
}
|
|
274
|
+
];
|
|
275
|
+
` },
|
|
276
|
+
{ file: 'auto-column-width.scss', language: 'scss', code: `@use "table-bro.api" as *;
|
|
277
|
+
|
|
278
|
+
@include bro-text-ellipsis-width('.bro-order_description', 400px);
|
|
279
|
+
@include bro-text-ellipsis-width('.bro-order_price', 120px);
|
|
280
|
+
` }
|
|
281
|
+
]
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
path: 'basics/tooltip',
|
|
285
|
+
name: 'Truncated text with hover tooltips',
|
|
286
|
+
text: `<!-- table-basics_tooltip -->
|
|
287
|
+
Each truncated table cell shows a tooltip on hover.`,
|
|
288
|
+
files: []
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
path: 'basics/animated-pagination',
|
|
292
|
+
name: 'Animated pagination',
|
|
293
|
+
text: `<!-- table-basics_animated-pagination -->
|
|
294
|
+
When paginating, the table animates all visible rows, with no vertical scrollbar.
|
|
295
|
+
|
|
296
|
+
<!-- animated-pagination-server -->
|
|
297
|
+
##### Animated pagination _with server-side data loading_
|
|
298
|
+
|
|
299
|
+
<!-- animated-pagination-server code -->
|
|
300
|
+
{{animated-pagination_server.ts}}`,
|
|
301
|
+
files: [
|
|
302
|
+
{ file: 'animated-pagination_server.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
303
|
+
|
|
304
|
+
const config: BroConfig = {
|
|
305
|
+
server: true
|
|
306
|
+
};
|
|
307
|
+
` }
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
path: 'basics/animated-page-size',
|
|
312
|
+
name: 'Animated page size change',
|
|
313
|
+
text: `<!-- table-basics_animated-page-size -->
|
|
314
|
+
The table uses animation when the number of visible rows changes.
|
|
315
|
+
|
|
316
|
+
<!-- animated-page-size-server -->
|
|
317
|
+
##### Animated page size change _with server-side data loading_
|
|
318
|
+
|
|
319
|
+
<!-- animated-page-size-server code -->
|
|
320
|
+
{{animated-page-size_server.ts}}`,
|
|
321
|
+
files: [
|
|
322
|
+
{ file: 'animated-page-size_server.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
323
|
+
|
|
324
|
+
const config: BroConfig = {
|
|
325
|
+
server: true
|
|
326
|
+
};
|
|
327
|
+
` }
|
|
328
|
+
]
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
path: 'basics/table-states',
|
|
332
|
+
name: 'Animated table states',
|
|
333
|
+
text: `<!-- intro -->
|
|
334
|
+
The table supports and animates following states: _Loading_, _No data_, _No records found_, _Failed_.
|
|
335
|
+
|
|
336
|
+
The first tables show the states as Lottie animations, as every table of this site with Angular Material and Bootstrap; the Native skin shows its own icons. The tables after them show the icons of an icon font.
|
|
337
|
+
|
|
338
|
+
<!-- table-state-loading-lottie -->
|
|
339
|
+
#### Loading _with Lottie_
|
|
340
|
+
|
|
341
|
+
State icon and message as a Lottie animation:
|
|
342
|
+
|
|
343
|
+
<!-- table-state-no-data-lottie -->
|
|
344
|
+
#### No data _with Lottie_
|
|
345
|
+
|
|
346
|
+
State icon and message as a Lottie animation:
|
|
347
|
+
|
|
348
|
+
<!-- table-state-no-records-found-lottie -->
|
|
349
|
+
#### No records found _with Lottie_
|
|
350
|
+
|
|
351
|
+
State icon and message as a Lottie animation:
|
|
352
|
+
|
|
353
|
+
<!-- table-state-failed-lottie -->
|
|
354
|
+
#### Failed _with Lottie_
|
|
355
|
+
|
|
356
|
+
State icon and message as a Lottie animation:
|
|
357
|
+
|
|
358
|
+
<!-- lottie -->
|
|
359
|
+
#### Lottie animations
|
|
360
|
+
|
|
361
|
+
Define the icons of the states as Lottie animations (\`statesDark\` are optional):
|
|
362
|
+
|
|
363
|
+
{{lottie-icon.ts}}
|
|
364
|
+
|
|
365
|
+
The Lottie files of these tables, light and dark, come with the package \`@table-bro/core\` in \`dist/lottie\`: \`mat\` for Angular Material and \`bs\` for Bootstrap. An Angular application serves them as its assets, in \`angular.json\`:
|
|
366
|
+
|
|
367
|
+
{{lottie-assets.json}}
|
|
368
|
+
|
|
369
|
+
**Dependencies** (384 kB):
|
|
370
|
+
|
|
371
|
+
{{lottie-dependencies.html}}
|
|
372
|
+
|
|
373
|
+
<!-- table-state-loading -->
|
|
374
|
+
#### Loading _with animated icon_
|
|
375
|
+
|
|
376
|
+
This is the default view displayed when there's no data yet:
|
|
377
|
+
|
|
378
|
+
<!-- table-state-no-data -->
|
|
379
|
+
#### No data _with animated icon_
|
|
380
|
+
|
|
381
|
+
When the data is loaded and it's empty (empty array \`[]\` in \`rows\`):
|
|
382
|
+
|
|
383
|
+
<!-- table-state-no-records-found -->
|
|
384
|
+
#### No records found _with animated icon_
|
|
385
|
+
|
|
386
|
+
When no records are found for the user's search:
|
|
387
|
+
|
|
388
|
+
<!-- table-basics_state-failed -->
|
|
389
|
+
#### Failed _with animated icon_
|
|
390
|
+
|
|
391
|
+
When an error occurs during data loading:
|
|
392
|
+
|
|
393
|
+
<!-- options -->
|
|
394
|
+
#### Other options to configure state icons and messages:
|
|
395
|
+
|
|
396
|
+
1. **Define icons as _HTML_**
|
|
397
|
+
|
|
398
|
+
_SVG_-icons:
|
|
399
|
+
|
|
400
|
+
{{html-icon_bs.ts}}
|
|
401
|
+
|
|
402
|
+
_Emoji_, as the Native skin shows them:
|
|
403
|
+
|
|
404
|
+
{{html-icon_nv.ts}}
|
|
405
|
+
|
|
406
|
+
2. **Define icons as _icon font_**, adding \`.bro-state-icon\` to tag-container:
|
|
407
|
+
|
|
408
|
+
Material icons:
|
|
409
|
+
|
|
410
|
+
{{font-icon_mat.ts}}
|
|
411
|
+
|
|
412
|
+
**Dependencies** (0.364 kB of \`CSS\`, 129 kB of \`woff2\`):
|
|
413
|
+
|
|
414
|
+
{{font-icon_mat-dependencies.html}}
|
|
415
|
+
|
|
416
|
+
Bootstrap icons:
|
|
417
|
+
|
|
418
|
+
{{font-icon_bs.ts}}
|
|
419
|
+
|
|
420
|
+
**Dependencies** (14.1 kB of \`CSS\`, 131 kB of \`woff2\`):
|
|
421
|
+
|
|
422
|
+
{{font-icon_bs-dependencies.html}}`,
|
|
423
|
+
files: [
|
|
424
|
+
{ file: 'html-icon_bs.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
425
|
+
|
|
426
|
+
const config: BroConfig = {
|
|
427
|
+
components: {
|
|
428
|
+
state: {
|
|
429
|
+
states: {
|
|
430
|
+
loading: [
|
|
431
|
+
\`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-rocket-takeoff-fill" viewBox="0 0 16 16">
|
|
432
|
+
<path d="M12.17 9.53c2.307-2.592 3.278-4.684 3.641-6.218.21-.887.214-1.58.16-2.065a3.6 3.6 0 0 0-.108-.563 2 2 0 0 0-.078-.23V.453c-.073-.164-.168-.234-.352-.295a2 2 0 0 0-.16-.045 4 4 0 0 0-.57-.093c-.49-.044-1.19-.03-2.08.188-1.536.374-3.618 1.343-6.161 3.604l-2.4.238h-.006a2.55 2.55 0 0 0-1.524.734L.15 7.17a.512.512 0 0 0 .433.868l1.896-.271c.28-.04.592.013.955.132.232.076.437.16.655.248l.203.083c.196.816.66 1.58 1.275 2.195.613.614 1.376 1.08 2.191 1.277l.082.202c.089.218.173.424.249.657.118.363.172.676.132.956l-.271 1.9a.512.512 0 0 0 .867.433l2.382-2.386c.41-.41.668-.949.732-1.526zm.11-3.699c-.797.8-1.93.961-2.528.362-.598-.6-.436-1.733.361-2.532.798-.799 1.93-.96 2.528-.361s.437 1.732-.36 2.531Z"/>
|
|
433
|
+
<path d="M5.205 10.787a7.6 7.6 0 0 0 1.804 1.352c-1.118 1.007-4.929 2.028-5.054 1.903-.126-.127.737-4.189 1.839-5.18.346.69.837 1.35 1.411 1.925"/>
|
|
434
|
+
</svg>\`, 'Loading'],
|
|
435
|
+
'no-data': [
|
|
436
|
+
\`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heartbreak-fill" viewBox="0 0 16 16">
|
|
437
|
+
<path d="M8.931.586 7 3l1.5 4-2 3L8 15C22.534 5.396 13.757-2.21 8.931.586M7.358.77 5.5 3 7 7l-1.5 3 1.815 4.537C-6.533 4.96 2.685-2.467 7.358.77"/>
|
|
438
|
+
</svg>\`, 'No data'],
|
|
439
|
+
'not-found': [
|
|
440
|
+
\`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
|
|
441
|
+
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
|
|
442
|
+
</svg>\`, 'No records found'],
|
|
443
|
+
fail: [
|
|
444
|
+
\`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-emoji-neutral-fill" viewBox="0 0 16 16">
|
|
445
|
+
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5m-3 4a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M10 8c-.552 0-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5S10.552 8 10 8"/>
|
|
446
|
+
</svg>\`, 'Failed']
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
` },
|
|
452
|
+
{ file: 'html-icon_nv.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
453
|
+
|
|
454
|
+
const config: BroConfig = {
|
|
455
|
+
components: {
|
|
456
|
+
state: {
|
|
457
|
+
states: {
|
|
458
|
+
loading: ['🚀', 'Loading'],
|
|
459
|
+
'no-data': ['💔', 'No data'],
|
|
460
|
+
'not-found': ['🔦', 'No records found'],
|
|
461
|
+
fail: ['😕', 'Failed']
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
` },
|
|
467
|
+
{ file: 'font-icon_mat.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
468
|
+
|
|
469
|
+
const config: BroConfig = {
|
|
470
|
+
components: {
|
|
471
|
+
fontIcon: {
|
|
472
|
+
html: (materialIcon: string) => \`<span class="material-icons bro-state-icon">\${materialIcon}</span>\`
|
|
473
|
+
},
|
|
474
|
+
state: {
|
|
475
|
+
fontIcon: true,
|
|
476
|
+
states: {
|
|
477
|
+
loading: ['front_loader', 'Loading'],
|
|
478
|
+
'no-data': ['heart_broken', 'No data'],
|
|
479
|
+
'not-found': ['heart_broken', 'No records found'],
|
|
480
|
+
fail: ['smart_toy', 'Failed']
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
` },
|
|
486
|
+
{ file: 'font-icon_mat-dependencies.html', language: 'html', code: `<head>
|
|
487
|
+
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
488
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
489
|
+
</head>
|
|
490
|
+
` },
|
|
491
|
+
{ file: 'font-icon_bs.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
492
|
+
|
|
493
|
+
const config: BroConfig = {
|
|
494
|
+
components: {
|
|
495
|
+
fontIcon: {
|
|
496
|
+
html: (iconName: string) => \`<i class="bi bi-\${iconName} bro-state-icon"></i>\`
|
|
497
|
+
},
|
|
498
|
+
state: {
|
|
499
|
+
fontIcon: true,
|
|
500
|
+
states: {
|
|
501
|
+
loading: ['rocket-takeoff-fill', 'Loading'],
|
|
502
|
+
'no-data': ['heartbreak-fill', 'No data'],
|
|
503
|
+
'not-found': ['search', 'No records found'],
|
|
504
|
+
fail: ['emoji-neutral-fill', 'Failed']
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
` },
|
|
510
|
+
{ file: 'font-icon_bs-dependencies.html', language: 'html', code: `<head>
|
|
511
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
512
|
+
</head>
|
|
513
|
+
` },
|
|
514
|
+
{ file: 'lottie-icon.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
515
|
+
|
|
516
|
+
const config: BroConfig = {
|
|
517
|
+
components: {
|
|
518
|
+
state: {
|
|
519
|
+
lottie: true,
|
|
520
|
+
|
|
521
|
+
lottieHTML: (iconSrc: string): string => \`<lottie-player autoplay mode="normal" src="\${iconSrc}"></lottie-player>\`,
|
|
522
|
+
|
|
523
|
+
states: {
|
|
524
|
+
loading: ['lottie/mat/loading.json', 'Loading'],
|
|
525
|
+
'no-data': ['lottie/mat/no-data.json', 'No data'],
|
|
526
|
+
'not-found': ['lottie/mat/no-data.json', 'No records found'],
|
|
527
|
+
fail: ['lottie/mat/fail.json', 'Failed']
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
statesDark: {
|
|
531
|
+
loading: ['lottie/mat/dark/loading.json', 'Loading'],
|
|
532
|
+
'no-data': ['lottie/mat/dark/no-data.json', 'No data'],
|
|
533
|
+
'not-found': ['lottie/mat/dark/no-data.json', 'No records found'],
|
|
534
|
+
fail: ['lottie/mat/dark/fail.json', 'Failed']
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
` },
|
|
540
|
+
{ file: 'lottie-assets.json', language: 'json', code: `"assets": [
|
|
541
|
+
{
|
|
542
|
+
"glob": "**/*",
|
|
543
|
+
"input": "node_modules/@table-bro/core/dist/lottie",
|
|
544
|
+
"output": "lottie"
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
` },
|
|
548
|
+
{ file: 'lottie-dependencies.html', language: 'html', code: `<body>
|
|
549
|
+
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
|
|
550
|
+
</body>
|
|
551
|
+
` }
|
|
552
|
+
]
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
path: 'design/css-styling',
|
|
556
|
+
name: 'Supports CSS styling of ‹table›',
|
|
557
|
+
text: `<!-- table-css -->
|
|
558
|
+
Table, Bro uses standard \`‹table›\` CSS styling, allowing you to apply any of your existing \`‹table›\` styles:
|
|
559
|
+
|
|
560
|
+
<!-- table-css code -->
|
|
561
|
+
{{table-css.ts}}
|
|
562
|
+
|
|
563
|
+
<!-- after -->
|
|
564
|
+
Your \`CSS\` of these classes, as of any \`‹table›\` of the page:
|
|
565
|
+
|
|
566
|
+
{{table-css.css}}`,
|
|
567
|
+
files: [
|
|
568
|
+
{ file: 'table-css.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
569
|
+
|
|
570
|
+
const config: BroConfig = {
|
|
571
|
+
css: {
|
|
572
|
+
table: {
|
|
573
|
+
className: '',
|
|
574
|
+
|
|
575
|
+
thead: {
|
|
576
|
+
className: '',
|
|
577
|
+
|
|
578
|
+
tr: {
|
|
579
|
+
className: 'bro-header-row',
|
|
580
|
+
|
|
581
|
+
th: {
|
|
582
|
+
className: 'bro-header-cell'
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
tbody: {
|
|
588
|
+
className: '',
|
|
589
|
+
|
|
590
|
+
tr: {
|
|
591
|
+
className: 'bro-row',
|
|
592
|
+
|
|
593
|
+
td: {
|
|
594
|
+
className: 'bro-cell'
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
|
|
600
|
+
shadow: {
|
|
601
|
+
className: 'shadow'
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
` },
|
|
606
|
+
{ file: 'table-css.css', language: 'css', code: `table-bro th.bro-header-cell,
|
|
607
|
+
table-bro td.bro-cell {
|
|
608
|
+
padding: 0 0 0 16px;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
table-bro tr.bro-header-row {
|
|
612
|
+
height: var(--bro-header-height);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
table-bro tr.bro-row {
|
|
616
|
+
height: var(--bro-row-height);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
table-bro table {
|
|
620
|
+
border-color: #dee2e6;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
table-bro.bro-dark-theme table {
|
|
624
|
+
border-color: #495057;
|
|
625
|
+
}
|
|
626
|
+
` }
|
|
627
|
+
]
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
path: 'design/custom-content',
|
|
631
|
+
name: 'Custom content in tables using Custom Elements',
|
|
632
|
+
text: `<!-- table-custom-content -->
|
|
633
|
+
You can use any component inside table cells, creating it as a \`Custom Element\`, or transforming your _Angular/React/Svelte/Vue/[Your framework](https://custom-elements-everywhere.com/)_ components to \`Custom Element\`, e.g.
|
|
634
|
+
|
|
635
|
+
<!-- after -->
|
|
636
|
+
Name your own elements of the cells \`bro-cell-*\`: the library never defines this prefix and leaves it to the application.
|
|
637
|
+
|
|
638
|
+
Switcher-component as a _[Custom Element](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)_:
|
|
639
|
+
|
|
640
|
+
{{bro-cell-switch.ts}}
|
|
641
|
+
|
|
642
|
+
and define it in your app and name it in the column of its cells:
|
|
643
|
+
|
|
644
|
+
{{bro-cell-switch_define.ts}}
|
|
645
|
+
|
|
646
|
+
Switcher-component as an _Angular_-component:
|
|
647
|
+
|
|
648
|
+
{{bro-cell-slide-toggle.component.ts}}
|
|
649
|
+
|
|
650
|
+
and define it in your app with \`@angular/elements\`, on the example of the page of the table:
|
|
651
|
+
|
|
652
|
+
{{bro-cell-slide-toggle.component_define.ts}}`,
|
|
653
|
+
files: [
|
|
654
|
+
{ file: 'bro-cell-switch.ts', language: 'ts', code: `import type {BroCellElement, BroCol, BroConfig, BroRow} from '@table-bro/core';
|
|
655
|
+
|
|
656
|
+
interface Order extends BroRow {
|
|
657
|
+
description: string;
|
|
658
|
+
price: number;
|
|
659
|
+
action: boolean;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export class BroCellSwitchComponent extends HTMLElement implements BroCellElement<Order> {
|
|
663
|
+
// The table sets the config, the column and the row of the cell before it connects the element
|
|
664
|
+
config?: BroConfig;
|
|
665
|
+
col?: BroCol<Order>;
|
|
666
|
+
row?: Order;
|
|
667
|
+
|
|
668
|
+
connectedCallback(): void {
|
|
669
|
+
const input = document.createElement('input');
|
|
670
|
+
|
|
671
|
+
input.type = 'checkbox';
|
|
672
|
+
input.role = 'switch';
|
|
673
|
+
input.checked = this.getAttribute('checked') === 'true';
|
|
674
|
+
|
|
675
|
+
this.replaceChildren(input);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
` },
|
|
679
|
+
{ file: 'bro-cell-switch_define.ts', language: 'ts', code: `import type {BroCol, BroRow} from '@table-bro/core';
|
|
680
|
+
|
|
681
|
+
import {BroCellSwitchComponent} from './bro-cell-switch';
|
|
682
|
+
|
|
683
|
+
interface Order extends BroRow {
|
|
684
|
+
description: string;
|
|
685
|
+
price: number;
|
|
686
|
+
action: boolean;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
customElements.define('bro-cell-switch', BroCellSwitchComponent);
|
|
690
|
+
|
|
691
|
+
const cols: BroCol<Order>[] = [
|
|
692
|
+
{
|
|
693
|
+
title: 'Description',
|
|
694
|
+
name: 'description'
|
|
695
|
+
}, {
|
|
696
|
+
title: 'Action',
|
|
697
|
+
name: 'action',
|
|
698
|
+
component: {
|
|
699
|
+
selector: 'bro-cell-switch',
|
|
700
|
+
attrs: {
|
|
701
|
+
checked: (_col, row) => row.action
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
];
|
|
706
|
+
` },
|
|
707
|
+
{ file: 'bro-cell-slide-toggle.component.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
|
|
708
|
+
|
|
709
|
+
import {MatSlideToggle} from '@angular/material/slide-toggle';
|
|
710
|
+
import type {BroCellElement, BroCol, BroRow} from '@table-bro/core';
|
|
711
|
+
|
|
712
|
+
interface Order extends BroRow {
|
|
713
|
+
description: string;
|
|
714
|
+
price: number;
|
|
715
|
+
action: boolean;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
@Component({
|
|
719
|
+
selector: 'bro-cell-slide-toggle',
|
|
720
|
+
template: \`
|
|
721
|
+
<mat-slide-toggle [hideIcon]="true" [checked]="row.action"></mat-slide-toggle>\`,
|
|
722
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
723
|
+
imports: [
|
|
724
|
+
MatSlideToggle
|
|
725
|
+
]
|
|
726
|
+
})
|
|
727
|
+
export class BroCellSlideToggleComponent implements BroCellElement<Order> {
|
|
728
|
+
@Input() col!: BroCol<Order>;
|
|
729
|
+
@Input() row!: Order;
|
|
730
|
+
}
|
|
731
|
+
` },
|
|
732
|
+
{ file: 'bro-cell-slide-toggle.component_define.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, inject, Injector, signal} from '@angular/core';
|
|
733
|
+
import {createCustomElement} from '@angular/elements';
|
|
734
|
+
|
|
735
|
+
import {TableBroNg} from '@table-bro/angular';
|
|
736
|
+
import type {BroCol, BroConfig, BroRow} from '@table-bro/core';
|
|
737
|
+
|
|
738
|
+
import {BroCellSlideToggleComponent} from './bro-cell-slide-toggle.component';
|
|
739
|
+
|
|
740
|
+
interface Order extends BroRow {
|
|
741
|
+
description: string;
|
|
742
|
+
price: number;
|
|
743
|
+
action: boolean;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
@Component({
|
|
747
|
+
selector: 'app-orders',
|
|
748
|
+
template: \`
|
|
749
|
+
<table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"></table-bro-ng>\`,
|
|
750
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
751
|
+
imports: [
|
|
752
|
+
TableBroNg
|
|
753
|
+
]
|
|
754
|
+
})
|
|
755
|
+
export class OrdersComponent {
|
|
756
|
+
cols = signal<BroCol<Order>[]>([
|
|
757
|
+
{
|
|
758
|
+
title: 'Description',
|
|
759
|
+
name: 'description'
|
|
760
|
+
}, {
|
|
761
|
+
title: 'Action',
|
|
762
|
+
name: 'action',
|
|
763
|
+
component: 'bro-cell-slide-toggle'
|
|
764
|
+
}
|
|
765
|
+
]);
|
|
766
|
+
|
|
767
|
+
rows = signal<Order[] | undefined>(undefined);
|
|
768
|
+
conf = signal<BroConfig>({skin: 'mat'});
|
|
769
|
+
|
|
770
|
+
private injector = inject(Injector);
|
|
771
|
+
|
|
772
|
+
constructor() {
|
|
773
|
+
this.defineCustomElements();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** The cells are Custom Elements: the table creates them by their tags, so they are defined before it. */
|
|
777
|
+
private defineCustomElements() {
|
|
778
|
+
if (customElements.get('bro-cell-slide-toggle')) {
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
customElements.define('bro-cell-slide-toggle', createCustomElement(BroCellSlideToggleComponent, {injector: this.injector}));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
` }
|
|
786
|
+
]
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
path: 'design/shadows',
|
|
790
|
+
name: 'Shadows',
|
|
791
|
+
text: `<!-- table-shadows -->
|
|
792
|
+
Shadows are enabled by default. They can be disabled:
|
|
793
|
+
|
|
794
|
+
<!-- table-shadows code -->
|
|
795
|
+
{{shadows-support.ts}}`,
|
|
796
|
+
files: [
|
|
797
|
+
{ file: 'shadows-support.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
798
|
+
|
|
799
|
+
const config: BroConfig = {
|
|
800
|
+
shadowsEnabled: false
|
|
801
|
+
};
|
|
802
|
+
` }
|
|
803
|
+
]
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
path: 'design/rounded-corners',
|
|
807
|
+
name: 'Rounded corners',
|
|
808
|
+
text: `<!-- table-rounded-corners -->
|
|
809
|
+
The table and the Filter Drawer have the rounded corners of Material 3: 12 px for the table, 16 px for the drawer.
|
|
810
|
+
|
|
811
|
+
Open details split the table into cards, and each card rounds its corners at the spaces between them.
|
|
812
|
+
|
|
813
|
+
<!-- table-rounded-corners-square -->
|
|
814
|
+
##### Square corners
|
|
815
|
+
|
|
816
|
+
A radius of \`0\` keeps the corners square:
|
|
817
|
+
|
|
818
|
+
<!-- table-rounded-corners-square code -->
|
|
819
|
+
Set the CSS variables on the table itself, the element \`‹table-bro›\` or \`tableId\` of the wrapper, and on the container of its Filter Drawer, without a new build of the skin:
|
|
820
|
+
|
|
821
|
+
{{rounded-corners.css}}
|
|
822
|
+
|
|
823
|
+
Or build the skin from its SCSS sources with square corners for all its tables:
|
|
824
|
+
|
|
825
|
+
{{rounded-corners.scss}}`,
|
|
826
|
+
files: [
|
|
827
|
+
{ file: 'rounded-corners.css', language: 'css', code: `#orders,
|
|
828
|
+
table-bro-filter-drawer-container,
|
|
829
|
+
table-bro-filter-drawer-container-ng {
|
|
830
|
+
--bro-corner-radius: 0;
|
|
831
|
+
--bro-filter-drawer-corner-radius: 0;
|
|
832
|
+
}
|
|
833
|
+
` },
|
|
834
|
+
{ file: 'rounded-corners.scss', language: 'scss', code: `// The skin of the project: "table-bro.material", "table-bro.bootstrap" or "table-bro.native"
|
|
835
|
+
@use "table-bro.material" with ($bro-corner-radius: 0, $bro-filter-drawer-corner-radius: 0);
|
|
836
|
+
` }
|
|
837
|
+
]
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
path: 'design/dark-light-mode',
|
|
841
|
+
name: 'Dark/Light mode',
|
|
842
|
+
text: `<!-- table-themes -->
|
|
843
|
+
You can set the value to \`auto\`, and the Table, Bro will follow the system color scheme defined by the \`prefers-color-scheme\` media query.
|
|
844
|
+
|
|
845
|
+
Alternatively, you can set it to \`light\` or \`dark\` to force a specific mode.
|
|
846
|
+
|
|
847
|
+
<!-- table-themes code -->
|
|
848
|
+
{{themes.ts}}`,
|
|
849
|
+
files: [
|
|
850
|
+
{ file: 'themes.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
851
|
+
|
|
852
|
+
const config: BroConfig = {
|
|
853
|
+
theme: 'auto' // 'light' | 'dark'
|
|
854
|
+
};
|
|
855
|
+
` }
|
|
856
|
+
]
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
path: 'design/material-design',
|
|
860
|
+
name: 'Material design',
|
|
861
|
+
text: `<!-- table-material-design -->
|
|
862
|
+
Angular Material skin of Table, Bro supports 100% of Google Material Design [data tables](https://m2.material.io/components/data-tables#specs) specs and integrates all the Angular Material components it depends on.
|
|
863
|
+
|
|
864
|
+
It supports Material 3 (M3) and Angular Material 22: the skin takes the theme of your application, as the components of Angular Material do, and a table in the dark theme takes the dark scheme of the theme.`,
|
|
865
|
+
files: []
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
path: 'design/bootstrap-design',
|
|
869
|
+
name: 'Bootstrap design',
|
|
870
|
+
text: `<!-- table-bootstrap-design -->
|
|
871
|
+
Bootstrap skin of Table, Bro supports 100% of Bootstrap [Tables](https://getbootstrap.com/docs/5.3/content/tables/) specs and integrates all the Bootstrap components it depends on.
|
|
872
|
+
|
|
873
|
+
It supports Bootstrap 5.3.`,
|
|
874
|
+
files: []
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
path: 'details/default-details',
|
|
878
|
+
name: 'Default Details',
|
|
879
|
+
text: `<!-- table-details-default code -->
|
|
880
|
+
{{default-details.ts}}
|
|
881
|
+
|
|
882
|
+
<!-- table-details-default-server -->
|
|
883
|
+
#### Default Details _with server-side data loading_
|
|
884
|
+
|
|
885
|
+
With default \`indeterminate\` progress bar:
|
|
886
|
+
|
|
887
|
+
<!-- table-details-default-server code -->
|
|
888
|
+
{{default-details_server.ts}}
|
|
889
|
+
|
|
890
|
+
<!-- table-details-default-server-determinate -->
|
|
891
|
+
With \`determinate\` progress bar:
|
|
892
|
+
|
|
893
|
+
<!-- table-details-default-server-determinate code -->
|
|
894
|
+
{{default-details_server-determinate.ts}}
|
|
895
|
+
|
|
896
|
+
<!-- emit -->
|
|
897
|
+
Example how to emit _progress status_ from your Details component (e.g. based on your server logic):
|
|
898
|
+
|
|
899
|
+
{{details_server-determinate_emit.ts}}
|
|
900
|
+
|
|
901
|
+
<!-- emit angular -->
|
|
902
|
+
or in case of Details is an Angular-component (registered in your Angular-app as a Custom Element):
|
|
903
|
+
|
|
904
|
+
{{details_server-determinate_emit-angular.ts}}`,
|
|
905
|
+
files: [
|
|
906
|
+
{ file: 'default-details.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
907
|
+
|
|
908
|
+
const config: BroConfig = {
|
|
909
|
+
rowDetailsComponent: 'bro-cell-details' // your Details-component as a Custom Element
|
|
910
|
+
};
|
|
911
|
+
` },
|
|
912
|
+
{ file: 'default-details_server.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
913
|
+
|
|
914
|
+
const config: BroConfig = {
|
|
915
|
+
rowDetailsComponent: 'bro-cell-details', // your Details-component as a Custom Element
|
|
916
|
+
|
|
917
|
+
rowDetailsServer: true
|
|
918
|
+
};
|
|
919
|
+
` },
|
|
920
|
+
{ file: 'default-details_server-determinate.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
921
|
+
|
|
922
|
+
const config: BroConfig = {
|
|
923
|
+
rowDetailsComponent: 'bro-cell-details', // your Details-component as a Custom Element
|
|
924
|
+
|
|
925
|
+
rowDetailsServer: true,
|
|
926
|
+
rowDetailsProgressMode: 'determinate'
|
|
927
|
+
};
|
|
928
|
+
` },
|
|
929
|
+
{ file: 'details_server-determinate_emit.ts', language: 'ts', code: `import {type BroRow, type BroRowDetailsElement, events} from '@table-bro/core';
|
|
930
|
+
|
|
931
|
+
interface Order extends BroRow {
|
|
932
|
+
description: string;
|
|
933
|
+
price: number;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export class BroCellDetailsComponent extends HTMLElement implements BroRowDetailsElement<Order> {
|
|
937
|
+
// The table sets the row before it connects the element, and the attribute server with rowDetailsServer
|
|
938
|
+
row?: Order;
|
|
939
|
+
|
|
940
|
+
private progress: number = 0;
|
|
941
|
+
// The table moves loaded server details from its aux row into the row of the details: the content renders once
|
|
942
|
+
private rendered: boolean = false;
|
|
943
|
+
private timerId?: ReturnType<typeof setInterval>;
|
|
944
|
+
|
|
945
|
+
connectedCallback(): void {
|
|
946
|
+
if (this.rendered) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if (this.getAttribute('server') !== 'true') {
|
|
951
|
+
this.render();
|
|
952
|
+
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Emulates the loading of the details: your server logic reports its progress from 0 to 1
|
|
957
|
+
this.timerId = setInterval(() => {
|
|
958
|
+
this.progress = Math.min(1, this.progress + 0.2);
|
|
959
|
+
|
|
960
|
+
if (this.progress === 1) {
|
|
961
|
+
clearInterval(this.timerId);
|
|
962
|
+
this.render();
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
this.emitProgress(this.progress);
|
|
966
|
+
}, 400);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
disconnectedCallback(): void {
|
|
970
|
+
clearInterval(this.timerId);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
private emitProgress(progress: number): void {
|
|
974
|
+
this.dispatchEvent(new CustomEvent(events.detailsServerLoadedEvent, {detail: progress}));
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
private render(): void {
|
|
978
|
+
this.textContent = \`Some details here: \${this.row?.description}\`;
|
|
979
|
+
this.rendered = true;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Somewhere in your app, before the table:
|
|
984
|
+
customElements.define('bro-cell-details', BroCellDetailsComponent);
|
|
985
|
+
` },
|
|
986
|
+
{ file: 'details_server-determinate_emit-angular.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, EventEmitter, inject, Injector, Input, type OnDestroy, type OnInit, Output, signal} from '@angular/core';
|
|
987
|
+
import {createCustomElement} from '@angular/elements';
|
|
988
|
+
|
|
989
|
+
import {TableBroNg} from '@table-bro/angular';
|
|
990
|
+
import {type BroCol, type BroConfig, type BroRow, type BroRowDetailsElement, events} from '@table-bro/core';
|
|
991
|
+
|
|
992
|
+
interface Order extends BroRow {
|
|
993
|
+
description: string;
|
|
994
|
+
price: number;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
@Component({
|
|
998
|
+
selector: 'bro-cell-order-details',
|
|
999
|
+
template: \`
|
|
1000
|
+
@if (loaded()) {
|
|
1001
|
+
<div>Some details here: {{ row.description }}</div>
|
|
1002
|
+
}\`,
|
|
1003
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1004
|
+
})
|
|
1005
|
+
export class BroCellOrderDetailsComponent implements OnInit, OnDestroy, BroRowDetailsElement<Order> {
|
|
1006
|
+
@Input() row!: Order;
|
|
1007
|
+
@Input() server?: string;
|
|
1008
|
+
|
|
1009
|
+
// The Custom Element of the component dispatches the output as the event of its name
|
|
1010
|
+
@Output(events.detailsServerLoadedEvent) progressChange = new EventEmitter<number>();
|
|
1011
|
+
|
|
1012
|
+
loaded = signal(false);
|
|
1013
|
+
|
|
1014
|
+
private progress: number = 0;
|
|
1015
|
+
private timerId?: ReturnType<typeof setInterval>;
|
|
1016
|
+
|
|
1017
|
+
ngOnInit() {
|
|
1018
|
+
if (this.server !== 'true') {
|
|
1019
|
+
this.loaded.set(true);
|
|
1020
|
+
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Emulates the loading of the details: your server logic reports its progress from 0 to 1
|
|
1025
|
+
this.timerId = setInterval(() => {
|
|
1026
|
+
this.progress = Math.min(1, this.progress + 0.2);
|
|
1027
|
+
|
|
1028
|
+
if (this.progress < 1) {
|
|
1029
|
+
this.progressChange.emit(this.progress);
|
|
1030
|
+
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
clearInterval(this.timerId);
|
|
1035
|
+
this.loaded.set(true);
|
|
1036
|
+
|
|
1037
|
+
// The next turn of the event loop: the details render their content before the table measures them
|
|
1038
|
+
setTimeout(() => this.progressChange.emit(1));
|
|
1039
|
+
}, 400);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
ngOnDestroy() {
|
|
1043
|
+
clearInterval(this.timerId);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
@Component({
|
|
1048
|
+
selector: 'app-orders',
|
|
1049
|
+
template: \`
|
|
1050
|
+
<table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"></table-bro-ng>\`,
|
|
1051
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1052
|
+
imports: [
|
|
1053
|
+
TableBroNg
|
|
1054
|
+
]
|
|
1055
|
+
})
|
|
1056
|
+
export class OrdersComponent {
|
|
1057
|
+
cols = signal<BroCol<Order>[]>([
|
|
1058
|
+
{
|
|
1059
|
+
title: 'Description',
|
|
1060
|
+
name: 'description'
|
|
1061
|
+
}
|
|
1062
|
+
]);
|
|
1063
|
+
|
|
1064
|
+
rows = signal<Order[] | undefined>(undefined);
|
|
1065
|
+
|
|
1066
|
+
conf = signal<BroConfig>({
|
|
1067
|
+
skin: 'mat',
|
|
1068
|
+
rowDetailsComponent: 'bro-cell-details', // your Details-component as a Custom Element
|
|
1069
|
+
|
|
1070
|
+
rowDetailsServer: true,
|
|
1071
|
+
rowDetailsProgressMode: 'determinate'
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
constructor() {
|
|
1075
|
+
// The table creates the details by their tag: the Custom Element is defined before it
|
|
1076
|
+
if (!customElements.get('bro-cell-details')) {
|
|
1077
|
+
customElements.define('bro-cell-details', createCustomElement(BroCellOrderDetailsComponent, {injector: inject(Injector)}));
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
` }
|
|
1082
|
+
]
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
path: 'filtering/toolbar-with-search',
|
|
1086
|
+
name: 'Toolbar with Search',
|
|
1087
|
+
text: `<!-- table-toolbar-with-search -->
|
|
1088
|
+
The toolbar starts with the search in every skin. Next to it go your buttons and one menu from \`components.toolbarMain.buttons\`: a button sends its \`eventType\` as the action of the \`bro-action\` event, an item of the menu sends its \`value\`. The tables of this site have the **Filters** button and the menu **More**:
|
|
1089
|
+
|
|
1090
|
+
<!-- table-toolbar-with-search code -->
|
|
1091
|
+
{{toolbar-buttons.ts}}
|
|
1092
|
+
|
|
1093
|
+
In the skin mat the menu is an optional element of the skin, which the application adds with \`withToolbarMenu()\`; the skins bs and nv always have it:
|
|
1094
|
+
|
|
1095
|
+
{{toolbar-buttons_app.config.ts}}
|
|
1096
|
+
|
|
1097
|
+
<!-- table-toolbar-with-search-server -->
|
|
1098
|
+
#### Toolbar with Search _with server-side data loading_
|
|
1099
|
+
|
|
1100
|
+
<!-- toolbar-off -->
|
|
1101
|
+
#### Without the toolbar or the header
|
|
1102
|
+
|
|
1103
|
+
\`toolbar: false\` renders the table without its toolbar, with its title, its search and its buttons. \`header: false\` hides the header of the table, the row of the titles of the columns: the header keeps its place in the DOM, and the widths and the sort of the columns stay as they are.
|
|
1104
|
+
|
|
1105
|
+
{{toolbar-off.ts}}`,
|
|
1106
|
+
files: [
|
|
1107
|
+
{ file: 'toolbar-buttons.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
|
|
1108
|
+
|
|
1109
|
+
import {TableBroNg} from '@table-bro/angular';
|
|
1110
|
+
import {type BroActionDetail, type BroCol, type BroConfig, type BroRow, events} from '@table-bro/core';
|
|
1111
|
+
|
|
1112
|
+
interface Order extends BroRow {
|
|
1113
|
+
description: string;
|
|
1114
|
+
price: number;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
@Component({
|
|
1118
|
+
selector: 'app-orders',
|
|
1119
|
+
template: \`
|
|
1120
|
+
<table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"
|
|
1121
|
+
(action)="onAction($event)"></table-bro-ng>\`,
|
|
1122
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1123
|
+
imports: [
|
|
1124
|
+
TableBroNg
|
|
1125
|
+
]
|
|
1126
|
+
})
|
|
1127
|
+
export class OrdersComponent {
|
|
1128
|
+
cols = signal<BroCol<Order>[]>([
|
|
1129
|
+
{
|
|
1130
|
+
title: 'Description',
|
|
1131
|
+
name: 'description'
|
|
1132
|
+
}
|
|
1133
|
+
]);
|
|
1134
|
+
|
|
1135
|
+
rows = signal<Order[] | undefined>(undefined);
|
|
1136
|
+
|
|
1137
|
+
conf = signal<BroConfig>({
|
|
1138
|
+
skin: 'mat',
|
|
1139
|
+
components: {
|
|
1140
|
+
toolbarMain: {
|
|
1141
|
+
buttons: [
|
|
1142
|
+
// A button sends its eventType; in the mat skin it is an icon with a tooltip
|
|
1143
|
+
{buttonTitle: 'Filters', eventType: events.filtersToggleEvent, iconHTML: 'filter_list', buttonTooltip: 'Filters'},
|
|
1144
|
+
// A menu: an item sends its value
|
|
1145
|
+
{
|
|
1146
|
+
buttonTitle: 'More',
|
|
1147
|
+
mode: 'navbar-dropdown',
|
|
1148
|
+
buttonTooltip: 'Your custom menu',
|
|
1149
|
+
options: [
|
|
1150
|
+
{value: 'action-1', title: 'Action', iconHTML: 'filter_1'},
|
|
1151
|
+
{value: 'action-2', title: 'Another action', iconHTML: 'filter_2'},
|
|
1152
|
+
{divider: true},
|
|
1153
|
+
{value: 'action-3', title: 'Something else here', iconHTML: 'filter_3'}
|
|
1154
|
+
]
|
|
1155
|
+
}
|
|
1156
|
+
]
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
onAction({action}: BroActionDetail<Order>) {
|
|
1162
|
+
if (action === 'action-1') {
|
|
1163
|
+
// your action
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
` },
|
|
1168
|
+
{ file: 'toolbar-buttons_app.config.ts', language: 'ts', code: `import type {ApplicationConfig} from '@angular/core';
|
|
1169
|
+
|
|
1170
|
+
import {provideTableBroMatSkin, withToolbarMenu} from '@table-bro/angular-material';
|
|
1171
|
+
|
|
1172
|
+
export const appConfig: ApplicationConfig = {
|
|
1173
|
+
providers: [
|
|
1174
|
+
provideTableBroMatSkin(withToolbarMenu())
|
|
1175
|
+
]
|
|
1176
|
+
};
|
|
1177
|
+
` },
|
|
1178
|
+
{ file: 'toolbar-off.ts', language: 'ts', code: `import type {BroConfig} from '@table-bro/core';
|
|
1179
|
+
|
|
1180
|
+
const config: BroConfig = {
|
|
1181
|
+
skin: 'mat',
|
|
1182
|
+
toolbar: false, // no toolbar
|
|
1183
|
+
header: false // no row of the titles of the columns
|
|
1184
|
+
};
|
|
1185
|
+
` }
|
|
1186
|
+
]
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
path: 'filtering/custom-user-filters',
|
|
1190
|
+
name: 'Custom user filters',
|
|
1191
|
+
text: `<!-- table-custom-user-filters code -->
|
|
1192
|
+
Your controls outside of the table give it their filters, and your rule \`filtersTransform\` filters the rows by them. In Angular the filters go to the input \`filter\` of the table:
|
|
1193
|
+
|
|
1194
|
+
{{custom-user-filters.ts}}
|
|
1195
|
+
|
|
1196
|
+
In Vanilla JS the page dispatches them to the table:
|
|
1197
|
+
|
|
1198
|
+
{{custom-user-filters_vanilla.ts}}
|
|
1199
|
+
|
|
1200
|
+
<!-- table-custom-user-filters-server -->
|
|
1201
|
+
#### Custom user filters _with server-side data loading_`,
|
|
1202
|
+
files: [
|
|
1203
|
+
{ file: 'custom-user-filters.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
|
|
1204
|
+
|
|
1205
|
+
import {MatSlideToggle, type MatSlideToggleChange} from '@angular/material/slide-toggle';
|
|
1206
|
+
import {TableBroNg} from '@table-bro/angular';
|
|
1207
|
+
import type {BroCol, BroConfig, BroFilters, BroRow} from '@table-bro/core';
|
|
1208
|
+
|
|
1209
|
+
interface Order extends BroRow {
|
|
1210
|
+
description: string;
|
|
1211
|
+
price: number;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
type OrderFilters = BroFilters<Order, {
|
|
1215
|
+
price50Plus?: boolean;
|
|
1216
|
+
}>;
|
|
1217
|
+
|
|
1218
|
+
@Component({
|
|
1219
|
+
selector: 'app-orders',
|
|
1220
|
+
template: \`
|
|
1221
|
+
<mat-slide-toggle (change)="onTogglePrice50Plus($event)">Price $50+</mat-slide-toggle>
|
|
1222
|
+
|
|
1223
|
+
<table-bro-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"
|
|
1224
|
+
[filter]="filter()"></table-bro-ng>\`,
|
|
1225
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1226
|
+
imports: [
|
|
1227
|
+
MatSlideToggle,
|
|
1228
|
+
TableBroNg
|
|
1229
|
+
]
|
|
1230
|
+
})
|
|
1231
|
+
export class OrdersComponent {
|
|
1232
|
+
cols = signal<BroCol<Order>[]>([
|
|
1233
|
+
{
|
|
1234
|
+
title: 'Description',
|
|
1235
|
+
name: 'description'
|
|
1236
|
+
}, {
|
|
1237
|
+
title: 'Price',
|
|
1238
|
+
name: 'price',
|
|
1239
|
+
dataType: 'number',
|
|
1240
|
+
className: 'bro-col_text-right'
|
|
1241
|
+
}
|
|
1242
|
+
]);
|
|
1243
|
+
|
|
1244
|
+
rows = signal<Order[] | undefined>(undefined);
|
|
1245
|
+
|
|
1246
|
+
conf = signal<BroConfig>({
|
|
1247
|
+
skin: 'mat',
|
|
1248
|
+
// Your rule for the rows by the filters of the input filter
|
|
1249
|
+
filtersTransform: (rows: Order[], filters: OrderFilters) => rows.filter(row => !filters.price50Plus || row.price >= 50)
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
filter = signal<OrderFilters | undefined>(undefined);
|
|
1253
|
+
|
|
1254
|
+
onTogglePrice50Plus(event: MatSlideToggleChange) {
|
|
1255
|
+
this.filter.set({price50Plus: event.checked});
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
` },
|
|
1259
|
+
{ file: 'custom-user-filters_vanilla.ts', language: 'ts', code: `import {type BroFilters, type BroRow, dispatchAction, events} from '@table-bro/core';
|
|
1260
|
+
|
|
1261
|
+
interface Order extends BroRow {
|
|
1262
|
+
description: string;
|
|
1263
|
+
price: number;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
type OrderFilters = BroFilters<Order, {
|
|
1267
|
+
price50Plus?: boolean;
|
|
1268
|
+
}>;
|
|
1269
|
+
|
|
1270
|
+
const table = document.querySelector('table-bro')!;
|
|
1271
|
+
const price50Plus = document.querySelector<HTMLInputElement>('#price-50-plus')!;
|
|
1272
|
+
|
|
1273
|
+
price50Plus.addEventListener('change', () => {
|
|
1274
|
+
const filters: OrderFilters = {price50Plus: price50Plus.checked};
|
|
1275
|
+
|
|
1276
|
+
dispatchAction(table, events.filtersFilterEvent, filters);
|
|
1277
|
+
});
|
|
1278
|
+
` }
|
|
1279
|
+
]
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
path: 'filtering/filter-drawer',
|
|
1283
|
+
name: 'Filter Drawer',
|
|
1284
|
+
text: `<!-- table-filter-drawer -->
|
|
1285
|
+
A drawer with your filters slides out next to the table. The filters live outside of the table. With Angular Material, \`‹table-bro-filter-drawer-container-ng›\` renders the table with the inputs of \`‹table-bro-ng›\` and your filters, its content, in a \`mat-drawer\`; the **Filters** button, which you add to the toolbar, opens it. With Bootstrap, Native, and in Vanilla JS, put the table and your filters with \`slot="filters"\` into \`‹table-bro-filter-drawer-container skin="bs"›\`; the **Filters** button opens it too.
|
|
1286
|
+
|
|
1287
|
+
Any button of your page opens the drawer with its \`toggle()\`.
|
|
1288
|
+
|
|
1289
|
+
<!-- table-filter-drawer code -->
|
|
1290
|
+
{{filter-drawer.ts}}
|
|
1291
|
+
|
|
1292
|
+
<!-- table-filter-drawer-server -->
|
|
1293
|
+
#### Filter Drawer _with server-side data loading_
|
|
1294
|
+
|
|
1295
|
+
In the server mode the table adds the filters of the drawer to its query: the \`bro-filters-server-loaded\` event carries a \`BroQuery\` with them in \`filter\`, together with the search, and with the sort.`,
|
|
1296
|
+
files: [
|
|
1297
|
+
{ file: 'filter-drawer.ts', language: 'ts', code: `import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
|
|
1298
|
+
|
|
1299
|
+
import {TableBroFilterDrawerContainerNg} from '@table-bro/angular-material';
|
|
1300
|
+
import {type BroCol, type BroConfig, type BroFilters, type BroRow, events, matchesFilterCondition} from '@table-bro/core';
|
|
1301
|
+
|
|
1302
|
+
interface Order extends BroRow {
|
|
1303
|
+
description: string;
|
|
1304
|
+
price: number;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
type OrderFilters = BroFilters<Order, {
|
|
1308
|
+
priceFrom?: number;
|
|
1309
|
+
priceTo?: number;
|
|
1310
|
+
}>;
|
|
1311
|
+
|
|
1312
|
+
// The filters live outside of the table, in mat-drawer next to it. The component renders <table-bro-ng> with its inputs
|
|
1313
|
+
// and outputs; the Filters button, which you add to the toolbar, opens the drawer; any other button calls its toggle().
|
|
1314
|
+
// With the bs and nv skins and in Vanilla JS put the table and your filters with slot="filters" into <table-bro-filter-drawer-container skin="bs">
|
|
1315
|
+
@Component({
|
|
1316
|
+
selector: 'app-orders',
|
|
1317
|
+
template: \`
|
|
1318
|
+
<table-bro-filter-drawer-container-ng [cols]="cols()" [rows]="rows()" [conf]="conf()"
|
|
1319
|
+
[filter]="filter()">
|
|
1320
|
+
<app-order-filters (changeFilter)="filter.set($event)"></app-order-filters>
|
|
1321
|
+
</table-bro-filter-drawer-container-ng>\`,
|
|
1322
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1323
|
+
imports: [
|
|
1324
|
+
TableBroFilterDrawerContainerNg
|
|
1325
|
+
]
|
|
1326
|
+
})
|
|
1327
|
+
export class OrdersComponent {
|
|
1328
|
+
cols = signal<BroCol<Order>[]>([
|
|
1329
|
+
{
|
|
1330
|
+
title: 'Description',
|
|
1331
|
+
name: 'description'
|
|
1332
|
+
}, {
|
|
1333
|
+
title: 'Price',
|
|
1334
|
+
name: 'price',
|
|
1335
|
+
dataType: 'number',
|
|
1336
|
+
className: 'bro-col_text-right'
|
|
1337
|
+
}
|
|
1338
|
+
]);
|
|
1339
|
+
|
|
1340
|
+
rows = signal<Order[] | undefined>(undefined);
|
|
1341
|
+
|
|
1342
|
+
conf = signal<BroConfig>({
|
|
1343
|
+
skin: 'mat',
|
|
1344
|
+
components: {
|
|
1345
|
+
toolbarMain: {
|
|
1346
|
+
buttons: [
|
|
1347
|
+
// The button that opens the drawer; in the mat skin it is an icon with a tooltip
|
|
1348
|
+
{buttonTitle: 'Filters', eventType: events.filtersToggleEvent, iconHTML: 'filter_list', buttonTooltip: 'Filters'}
|
|
1349
|
+
]
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
// Local mode: your rule for the rows. In the server mode the filters come in \`query.filter\` of the load event
|
|
1353
|
+
filtersTransform: (rows: Order[], filters: OrderFilters) => rows.filter(row => {
|
|
1354
|
+
return matchesFilterCondition(row.price, {gte: filters.priceFrom, lte: filters.priceTo});
|
|
1355
|
+
})
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
// A cleared filter goes as an empty value: {priceFrom: undefined}
|
|
1359
|
+
filter = signal<OrderFilters | undefined>(undefined);
|
|
1360
|
+
}
|
|
1361
|
+
` }
|
|
1362
|
+
]
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
path: 'filtering/custom-sorting-rules',
|
|
1366
|
+
name: 'Custom sorting rules',
|
|
1367
|
+
text: `<!-- table-custom-sorting-rules -->
|
|
1368
|
+
You can specify a \`sortKey\` for each column to control the sorting behavior.
|
|
1369
|
+
|
|
1370
|
+
For example, the "Date" column can display a formatted date (model's \`date\`), while sorting is performed based on the model's underlying \`timestamp\` field.
|
|
1371
|
+
|
|
1372
|
+
<!-- table-custom-sorting-rules code -->
|
|
1373
|
+
{{custom-sorting-rules.ts}}
|
|
1374
|
+
|
|
1375
|
+
<!-- table-custom-sorting-rules-server -->
|
|
1376
|
+
#### Custom sorting rules _with server-side data loading_`,
|
|
1377
|
+
files: [
|
|
1378
|
+
{ file: 'custom-sorting-rules.ts', language: 'ts', code: `import type {BroCol, BroRow} from '@table-bro/core';
|
|
1379
|
+
|
|
1380
|
+
interface Order extends BroRow {
|
|
1381
|
+
date: string; // the formatted date that the column shows
|
|
1382
|
+
timestamp: number; // the date that the column sorts by
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const cols: BroCol<Order>[] = [
|
|
1386
|
+
{
|
|
1387
|
+
title: 'Date',
|
|
1388
|
+
name: 'date',
|
|
1389
|
+
sortKey: 'timestamp'
|
|
1390
|
+
}
|
|
1391
|
+
];
|
|
1392
|
+
` }
|
|
1393
|
+
]
|
|
1394
|
+
}
|
|
1395
|
+
];
|
|
1396
|
+
/** The text of a page as Markdown: without the marks of its parts, with its files of code in code blocks. */
|
|
1397
|
+
export function docPageMarkdown(page) {
|
|
1398
|
+
return page.text
|
|
1399
|
+
.replace(/^<!-- .+ -->\n(\n)?/gm, '')
|
|
1400
|
+
.replace(/^( *)\{\{(.+)\}\}$/gm, (line, indent, file) => {
|
|
1401
|
+
const code = page.files.find(item => item.file === file);
|
|
1402
|
+
return `${indent}${file}:\n\n${indent}\`\`\`${code.language}\n${code.code.trimEnd().replace(/^(?=.)/gm, indent)}\n${indent}\`\`\``;
|
|
1403
|
+
});
|
|
1404
|
+
}
|