@oneteme/jquery-table 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +439 -122
  2. package/esm2022/lib/component/export.manager.mjs +45 -0
  3. package/esm2022/lib/component/group-by.manager.mjs +99 -0
  4. package/esm2022/lib/component/lazy-column-manager.mjs +99 -0
  5. package/esm2022/lib/component/slice-panel/slice-panel.component.mjs +43 -37
  6. package/esm2022/lib/component/slice-panel/slice-panel.model.mjs +1 -1
  7. package/esm2022/lib/component/table-preferences.manager.mjs +38 -0
  8. package/esm2022/lib/component/table.component.mjs +681 -370
  9. package/esm2022/lib/component/table.constants.mjs +31 -0
  10. package/esm2022/lib/component/table.utils.mjs +2 -2
  11. package/esm2022/lib/component/view/view.facade.mjs +298 -0
  12. package/esm2022/lib/jqt-i18n.token.mjs +41 -0
  13. package/esm2022/lib/jquery-table.model.mjs +14 -2
  14. package/esm2022/public-api.mjs +2 -1
  15. package/fesm2022/oneteme-jquery-table.mjs +1377 -408
  16. package/fesm2022/oneteme-jquery-table.mjs.map +1 -1
  17. package/lib/component/export.manager.d.ts +16 -0
  18. package/lib/component/group-by.manager.d.ts +44 -0
  19. package/lib/component/lazy-column-manager.d.ts +26 -0
  20. package/lib/component/slice-panel/slice-panel.component.d.ts +11 -3
  21. package/lib/component/slice-panel/slice-panel.model.d.ts +11 -1
  22. package/lib/component/table-preferences.manager.d.ts +17 -0
  23. package/lib/component/table.component.d.ts +162 -27
  24. package/lib/component/table.constants.d.ts +30 -0
  25. package/lib/component/view/view.facade.d.ts +93 -0
  26. package/lib/jqt-i18n.token.d.ts +87 -0
  27. package/lib/jquery-table.model.d.ts +79 -0
  28. package/package.json +2 -2
  29. package/public-api.d.ts +1 -0
package/README.md CHANGED
@@ -1,16 +1,24 @@
1
- # jQuery-Table
1
+ # @oneteme/jquery-table
2
2
 
3
3
  Bibliothèque Angular qui encapsule Angular Material Table avec une API orientée configuration runtime.
4
4
 
5
5
  ## Features
6
6
 
7
- - Colonnes dynamiques (ajout/retrait à l'exécution)
8
- - Liste de colonnes optionnelles ajoutables via bouton `+`
9
- - Slice catégories à gauche (ex: status)
10
- - Filtrage temps réel selon la catégorie sélectionnée
11
- - Settings par colonne (⋯): filter by values + actions custom
12
- - Actions utilitaires custom par colonne (événements)
13
- - Intégration avec `@oneteme/jquery-core` (`field`, `DataProvider`)
7
+ - Configuration déclarative du tableau via `TableProvider`
8
+ - Colonnes optionnelles ajoutables/supprimables à l'exécution via menu View
9
+ - Réordonnancement des colonnes par glisser-déposer
10
+ - Tri natif Material par colonne
11
+ - Recherche textuelle temps réel
12
+ - Pagination standard et pagination par groupe
13
+ - **Group by** : regroupement des lignes par colonne, groupes pliables/dépliables, pagination indépendante par groupe
14
+ - **Slice panel** : filtres catégoriels latéraux, statiques (prédéfinis) ou dynamiques (colonne valeurs uniques), multi-sélection configurable
15
+ - **Chargement paresseux** (lazy) de cellules via Observable par colonne
16
+ - Rendus de cellule personnalisés via directive `jqtCellDef`
17
+ - État de chargement (`isLoading`) et état vide configurable
18
+ - Événement de clic sur ligne (`rowSelected`)
19
+ - CSS variables pour la personnalisation visuelle complète
20
+ - Compatible mode allégé : `[dataSource]` + `[displayedColumns]` sans `config`
21
+ - Intégration `@oneteme/jquery-core` (`DataProvider`)
14
22
 
15
23
  ## Installation
16
24
 
@@ -18,174 +26,483 @@ Bibliothèque Angular qui encapsule Angular Material Table avec une API orienté
18
26
  npm install @oneteme/jquery-core @oneteme/jquery-table @angular/material @angular/cdk
19
27
  ```
20
28
 
29
+ ---
30
+
21
31
  ## Utilisation rapide
22
32
 
23
33
  ```ts
24
- import { Component } from '@angular/core';
25
34
  import { TableComponent, TableProvider, col } from '@oneteme/jquery-table';
26
35
 
27
36
  interface Row {
28
- issue: string;
29
- status: 'Backlog' | 'In Progress' | 'Done';
30
- owner: string;
31
- priority: 'Low' | 'Medium' | 'High';
37
+ id: number;
38
+ name: string;
39
+ status: 'active' | 'inactive';
40
+ team: string;
32
41
  }
33
42
 
34
43
  @Component({
35
- standalone: true,
36
- imports: [TableComponent],
37
- template: `
38
- <jquery-table
39
- [config]="tableConfig"
40
- (columnAdded)="onColumnAdded($event)"
41
- (columnRemoved)="onColumnRemoved($event)"
42
- (categorySelected)="onCategorySelected($event)"
43
- ></jquery-table>
44
- `,
44
+ imports: [TableComponent],
45
+ template: `<jquery-table [config]="table" [data]="rows" (rowSelected)="onRow($event)"></jquery-table>`,
45
46
  })
46
47
  export class DemoComponent {
47
- tableConfig: TableProvider<Row> = {
48
- title: 'Ticket board',
49
- showAddColumnButton: true,
50
- enableColumnRemoval: true,
51
- columns: [col<Row>('issue', 'Issue'), col<Row>('status', 'Status')],
52
- availableColumns: [col<Row>('owner', 'Owner'), col<Row>('priority', 'Priority')],
53
- categorySlice: {
54
- title: 'Status',
55
- allLabel: 'Tous',
56
- categories: [
57
- { key: 'backlog', label: 'Backlog', filter: (row) => row.status === 'Backlog' },
58
- { key: 'in-progress', label: 'In Progress', filter: (row) => row.status === 'In Progress' },
59
- { key: 'done', label: 'Done', filter: (row) => row.status === 'Done' },
60
- ],
61
- },
62
- data: [
63
- { issue: 'Fix auth', status: 'In Progress', owner: 'Anna', priority: 'High' },
64
- { issue: 'Improve docs', status: 'Backlog', owner: 'Leo', priority: 'Low' },
65
- ],
66
- };
67
-
68
- onColumnAdded() {}
69
- onColumnRemoved() {}
70
- onCategorySelected() {}
48
+ rows: Row[] = [];
49
+
50
+ table: TableProvider<Row> = {
51
+ title: 'Utilisateurs',
52
+ columns: [
53
+ col('id', 'ID'),
54
+ col('name', 'Nom', { sortable: true }),
55
+ col('status', 'Statut', { sliceable: true }),
56
+ col('team', 'Équipe', { groupable: true, optional: true }),
57
+ ],
58
+ slices: [
59
+ {
60
+ title: 'Statut',
61
+ columnKey: 'status',
62
+ categories: [
63
+ { key: 'active', label: 'Actif', filter: r => r.status === 'active' },
64
+ { key: 'inactive', label: 'Inactif', filter: r => r.status === 'inactive' },
65
+ ],
66
+ },
67
+ ],
68
+ search: { enabled: true },
69
+ pagination: { enabled: true, pageSize: 20 },
70
+ view: { enabled: true, enableColumnDragDrop: true },
71
+ export: { enabled: true, filename: 'utilisateurs' },
72
+ };
73
+
74
+ onRow(row: Row) { console.log(row); }
71
75
  }
72
76
  ```
73
77
 
74
- ## Migration minimale depuis Angular Material Table
78
+ ---
75
79
 
76
- Si votre composant utilise déjà `dataSource` et `displayedColumns`, vous pouvez migrer avec très peu de changements:
80
+ ## Mode allégé compatibilité `dataSource` / `displayedColumns`
77
81
 
78
- ```html
79
- <!-- Avant -->
80
- <!-- <table mat-table [dataSource]="dataSource"> ... -->
82
+ Pour une migration minimale depuis une `mat-table` existante :
81
83
 
82
- <!-- Après -->
84
+ ```html
83
85
  <jquery-table
84
- [dataSource]="dataSource"
85
- [displayedColumns]="displayedColumns"
86
- [columnLabels]="columnLabels"
86
+ [dataSource]="rows"
87
+ [displayedColumns]="['id', 'name', 'status']"
88
+ [columnLabels]="{ id: 'ID', name: 'Nom', status: 'Statut' }"
89
+ [isLoading]="loading"
87
90
  ></jquery-table>
88
91
  ```
89
92
 
93
+ Le `config` complet reste disponible pour activer les fonctionnalités avancées.
94
+
95
+ ---
96
+
97
+ ## API `TableComponent`
98
+
99
+ ### Inputs
100
+
101
+ | Propriété | Type | Description |
102
+ |-----------|------|-------------|
103
+ | `config` | `TableProvider<T>` | Configuration principale (colonnes, données, comportements) |
104
+ | `data` | `T[]` | Données en entrée directe (remplace `config.data`) |
105
+ | `dataSource` | `T[] \| { data: T[] }` | Alias compatibilité Material |
106
+ | `displayedColumns` | `string[]` | Alias compatibilité Material clés de colonnes à afficher |
107
+ | `columnsConfig` | `TableColumnProvider<T>[]` | Configuration des colonnes (remplace `config.columns`) |
108
+ | `columnLabels` | `Record<string, string>` | Labels des colonnes pour le mode allégé |
109
+ | `isLoading` | `boolean` | Affiche l'état de chargement (défaut : `false`) |
110
+ | `view` | `TableViewConfig` | Surcharge `config.view` si défini (mode HTML) |
111
+
112
+ ### Outputs
113
+
114
+ | Événement | Payload | Description |
115
+ |-----------|---------|-------------|
116
+ | `rowSelected` | `T` | Ligne cliquée |
117
+ | `columnAdded` | `TableColumnProvider<T>` | Colonne ajoutée aux colonnes actives |
118
+ | `columnRemoved` | `TableColumnProvider<T>` | Colonne retirée des colonnes actives |
119
+ | `categorySelected` | `string` | Catégorie de slice sélectionnée |
120
+ | `addRequested` | `void` | Clic sur le bouton d'ajout de colonne |
121
+
122
+ ---
123
+
124
+ ## API — `TableProvider<T>`
125
+
90
126
  ```ts
91
- displayedColumns = ['issue', 'status', 'owner'];
127
+ interface TableProvider<T = any> {
128
+ columns?: TableColumnProvider<T>[];
129
+ title?: string;
92
130
 
93
- columnLabels = {
94
- issue: 'Issue',
95
- status: 'Status',
96
- owner: 'Owner',
97
- };
131
+ slices?: SliceConfig<T>[]; // Filtres catégoriels (slice panel)
132
+ enableSliceToggle?: boolean; // Défaut : true
133
+
134
+ search?: TableSearchConfig;
135
+ pagination?: TablePaginationConfig;
136
+ view?: TableViewConfig;
137
+ labels?: TableLabelsConfig;
138
+ export?: TableExportConfig<T>;
139
+
140
+ defaultSort?: { active: string; direction: 'asc' | 'desc' };
141
+ rowClass?: (row: T, index: number) => string | string[] | Record<string, boolean>;
142
+ }
98
143
  ```
99
144
 
100
- Le `config` complet reste disponible pour activer les fonctionnalités avancées (slice catégories, ajout/retrait de colonnes, menu d’options).
145
+ > **Note** : les données (`data`) et l’état de chargement (`isLoading`) sont des `@Input` séparés
146
+ > sur le composant, pas des champs de `TableProvider`.
101
147
 
102
- ## API
148
+ ---
103
149
 
104
- ### Inputs
150
+ ## API — `TableColumnProvider<T>`
151
+
152
+ ```ts
153
+ interface TableColumnProvider<T = any> {
154
+ key: string;
155
+ header?: string; // Label de l'entête
156
+ icon?: string; // Icône Material (affiché dans l'entête)
157
+ value?: DataProvider<any>; // (row, index) => valeur affichée
158
+ sortValue?: DataProvider<any>; // Valeur pour le tri si différente de value
159
+ sortable?: boolean;
160
+ width?: string; // Largeur CSS en px uniquement pour le calcul min-width (ex: '120px')
161
+ // Les unités %, em, rem sont ignorées dans le calcul de tableMinWidth
162
+
163
+ optional?: boolean; // Colonne masquée par défaut, ajoutables via View menu
164
+ removable?: boolean; // Peut être retirée manuellement
165
+
166
+ groupable?: boolean; // Disponible comme critère de regroupement dans View menu
167
+ sliceable?: boolean; // Disponible comme slice dynamique dans View menu
168
+ searchValue?: DataProvider<string>; // Valeur utilisée par la recherche texte (si différente de value)
169
+
170
+ lazy?: {
171
+ fetchFn: () => Observable<any[]>; // Retourne un tableau dans le même ordre que les lignes
172
+ };
173
+ }
174
+ ```
105
175
 
106
- - `dataSource`: alias compatible Material (`T[]` ou `{ data: T[] }`)
107
- - `displayedColumns`: alias compatible Material (`string[]`)
108
- - `columnLabels`: labels optionnels pour `displayedColumns`
109
- - `config.showColumnSettings`: active les menus `⋯` en header de colonne
110
- - `config.maxFilterValuesPerColumn`: limite le nombre de valeurs proposées dans `Filter by values`
111
- - `config.columnUtilityActions`: actions custom globales par colonne
112
- - `config.columns`: colonnes initiales visibles
113
- - `config.optionalColumns` (ou `config.availableColumns`): colonnes optionnelles ajoutables via `+`
114
- - `config.showAddColumnButton`: active/désactive le bouton `+`
115
- - `config.showOptionsButton`: active/désactive le bouton `Options`
116
- - `config.bottomActionsLabel`: libellé des actions dockées en bas-gauche
117
- - `config.enableColumnRemoval`: autorise la suppression des colonnes
118
- - `config.categorySlice`: configuration du panel de catégories à gauche
119
- - `config.data`: dataset source
120
-
121
- ### Smart adaptation (intelligence data-driven)
122
- Le wrapper est volontairement simple: pas de système d'inférence automatique complexe.
123
- Le développeur fournit explicitement les colonnes de base, les colonnes optionnelles et les filtres.
176
+ ---
124
177
 
125
- ### Outputs
178
+ ## Helper `col()`
179
+
180
+ Raccourci pour déclarer une colonne sans verbosité :
181
+
182
+ ```ts
183
+ import { col } from '@oneteme/jquery-table';
184
+
185
+ columns: [
186
+ col('name', 'Nom'),
187
+ col('status', 'Statut', { sortable: false, groupable: true }),
188
+ col('team', 'Équipe', { optional: true }),
189
+ ]
190
+ ```
191
+
192
+ ---
193
+
194
+ ## API — Sous-configurations
195
+
196
+ ### `TableSearchConfig`
126
197
 
127
- - `columnAdded`: émis après ajout effectif d'une colonne
128
- - `columnRemoved`: émis après suppression effective d'une colonne
129
- - `categorySelected`: émis au changement de catégorie
130
- - `columnSettingSelected`: émis au clic sur une action de menu colonne (built-in ou custom)
131
- - `optionSelected`: émis depuis le menu d'options
198
+ ```ts
199
+ interface TableSearchConfig {
200
+ enabled?: boolean;
201
+ initialQuery?: string; // Terme de recherche pré-rempli au chargement
202
+ searchColumns?: string[]; // Restreint la recherche à ces clés de colonnes
203
+ }
204
+ ```
205
+
206
+ ### `TablePaginationConfig`
207
+
208
+ ```ts
209
+ interface TablePaginationConfig {
210
+ enabled?: boolean;
211
+ pageSize?: number;
212
+ pageSizeOptions?: number[];
213
+ pageSizeOptionsGroupBy?: number[]; // Options spécifiques au mode Group by
214
+ }
215
+ ```
216
+
217
+ ### `TableViewConfig`
218
+
219
+ ```ts
220
+ interface TableViewConfig {
221
+ enabled?: boolean; // Défaut : false active le bouton View
222
+ enableColumnRemoval?: boolean; // Défaut : true autorise la suppression de colonnes
223
+ enableColumnDragDrop?: boolean; // Défaut : false active le réordonnancement drag & drop
224
+ }
225
+ ```
226
+
227
+ ### `TableLabelsConfig`
228
+
229
+ ```ts
230
+ interface TableLabelsConfig {
231
+ empty?: string; // Défaut : 'Aucune donnée' (ou la valeur i18n injectée)
232
+ loading?: string; // Défaut : 'Chargement des données...' (ou la valeur i18n injectée)
233
+ }
234
+ ```
235
+
236
+ ### `TableExportConfig<T>`
237
+
238
+ ```ts
239
+ interface TableExportConfig<T = any> {
240
+ enabled?: boolean; // Affiche le bouton Export
241
+ filename?: string; // Nom du fichier sans extension (défaut : 'export')
242
+ transform?: (row: T) => Record<string, string>; // Transforme chaque ligne avant export
243
+ }
244
+ ```
245
+
246
+ L’export ne porte que sur les **données filtrées** (slice + recherche) et les **colonnes visibles**.
247
+ Le fichier est généré avec un BOM UTF-8 pour une compatibilité Excel correcte.
132
248
 
133
- ### Exemple settings colonne
249
+ ---
250
+
251
+ ## i18n
252
+
253
+ Tous les labels de l’interface sont personnalisables via un `InjectionToken` :
134
254
 
135
255
  ```ts
136
- tableConfig = {
137
- showColumnSettings: true,
138
- maxFilterValuesPerColumn: 12,
139
- columnUtilityActions: [
140
- { action: 'utility:copyColumnKey', label: 'Copy column key' },
141
- ],
256
+ import { JQT_I18N } from '@oneteme/jquery-table';
257
+
258
+ // Dans AppModule ou un provider racine
259
+ providers: [
260
+ {
261
+ provide: JQT_I18N,
262
+ useValue: {
263
+ searchPlaceholder: 'Search…',
264
+ emptyState: 'No data',
265
+ loadingState: 'Loading…',
266
+ viewNoneSelected: 'None',
267
+ groupRemove: 'Remove grouping',
268
+ exportButtonLabel: 'Export',
269
+ },
270
+ },
271
+ ]
272
+ ```
273
+
274
+ Seuls les champs fournis écrasent les défauts français. Voir `JqtI18n` pour la liste complète (24 labels).
275
+
276
+ ---
277
+
278
+ ## Fonctionnalité — Group by
279
+
280
+ Le regroupement s'active en ajoutant `groupable: true` sur une ou plusieurs colonnes. L'utilisateur choisit ensuite la colonne de regroupement depuis le menu View.
281
+
282
+ ```ts
283
+ columns: [
284
+ { key: 'team', header: 'Équipe', groupable: true },
285
+ { key: 'status', header: 'Statut', groupable: true },
286
+ { key: 'name', header: 'Nom' },
287
+ ],
288
+ view: { enabled: true },
289
+ pagination: {
290
+ enabled: true,
291
+ pageSizeOptionsGroupBy: [5, 10, 25],
292
+ },
293
+ ```
294
+
295
+ Comportements inclus :
296
+ - Groupes pliables/dépliables individuellement
297
+ - Pagination indépendante par groupe
298
+ - Tri des entêtes de groupe (asc / desc)
299
+ - Scroll automatique vers l'entête du groupe actif
300
+
301
+ ---
302
+
303
+ ## Fonctionnalité — Slice panel
304
+
305
+ Le slice panel est un panneau latéral de filtres catégoriels.
306
+
307
+ ### Slices statiques (catégories prédéfinies)
308
+
309
+ ```ts
310
+ slices: [
311
+ {
312
+ title: 'Statut',
313
+ icon: 'circle',
314
+ columnKey: 'status',
315
+ categories: [
316
+ { key: 'active', label: 'Actif', filter: r => r.status === 'active' },
317
+ { key: 'inactive', label: 'Inactif', filter: r => r.status === 'inactive' },
318
+ ],
319
+ },
320
+ {
321
+ title: 'Priorité',
322
+ columnKey: 'priority',
323
+ multiSelect: true,
324
+ bucket: r => r.priority, // Auto-génère les catégories depuis les valeurs uniques
325
+ },
326
+ ],
327
+ ```
328
+
329
+ ### Slices dynamiques (depuis le menu View)
330
+
331
+ Une colonne avec `sliceable: true` peut être ajoutée dynamiquement comme slice depuis le menu View.
332
+
333
+ ```ts
334
+ columns: [
335
+ { key: 'team', header: 'Équipe', sliceable: true },
336
+ ],
337
+ view: { enabled: true },
338
+ ```
339
+
340
+ ### `SliceConfig<T>`
341
+
342
+ ```ts
343
+ interface SliceConfig<T = any> {
344
+ title?: string;
345
+ icon?: string;
346
+ columnKey?: string;
347
+ multiSelect?: boolean; // Défaut : false
348
+ hidden?: boolean; // Masqué par défaut dans le panel
349
+ categories?: SliceCategory<T>[];
350
+ bucket?: (row: T) => string; // Auto-génère les catégories (ignoré si categories défini)
351
+ }
352
+
353
+ interface SliceCategory<T = any> {
354
+ key: string;
355
+ label: string;
356
+ filter: (row: T) => boolean;
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Fonctionnalité — lazy columns
363
+
364
+ ```ts
365
+ columns: [
366
+ {
367
+ key: 'details',
368
+ header: 'Détails',
369
+ lazy: {
370
+ fetchFn: () => this.api.getDetails(),
371
+ },
372
+ },
373
+ ],
374
+ ```
375
+
376
+ Le composant gère l'état `idle | loading | loaded | error` par colonne, avec possibilité de relancer la requête en cas d'erreur.
377
+
378
+ ---
379
+
380
+ ## Fonctionnalité — Cellules personnalisées (`jqtCellDef`)
381
+
382
+ ```html
383
+ <jquery-table [config]="table">
384
+
385
+ <ng-template jqtCellDef="status" let-row>
386
+ <span [class]="'badge badge-' + row.status">{{ row.status }}</span>
387
+ </ng-template>
388
+
389
+ <ng-template jqtCellDef="actions" let-row let-index="index">
390
+ <button (click)="edit(row)">Éditer</button>
391
+ </ng-template>
392
+
393
+ </jquery-table>
394
+ ```
395
+
396
+ Contexte du template :
397
+ - `$implicit` la ligne brute (`T`)
398
+ - `index` index global de la ligne
399
+
400
+ ---
401
+
402
+ ## Fonctionnalité — Clic sur ligne
403
+
404
+ ```html
405
+ <jquery-table [config]="table" (rowSelected)="onRow($event)"></jquery-table>
406
+ ```
407
+
408
+ La ligne brute `T` est émise via l'Output `(rowSelected)`. Pour appliquer une classe conditionnelle aux lignes, utiliser `rowClass` dans `config` :
409
+
410
+ ```ts
411
+ config: TableProvider<Row> = {
412
+ rowClass: (row) => row.status === 'error' ? 'row-error' : '',
142
413
  };
143
414
  ```
144
415
 
145
- ## Personnalisation visuelle (CSS variables)
416
+ ---
417
+
418
+ ## Usage standalone — `SlicePanelComponent`
146
419
 
147
- Toutes les couleurs principales sont exposées via des CSS custom properties sur `:host`.
148
- Pour les surcharger, ciblez `jquery-table` dans votre feuille de styles globale :
420
+ `SlicePanelComponent` est exporté et utilisable indépendamment du tableau, par exemple pour filtrer les données d'un graphique :
421
+
422
+ ```html
423
+ <jqt-slice-panel
424
+ [sliceConfigs]="slices"
425
+ [columns]="columns"
426
+ [data]="rows"
427
+ [showCounts]="true"
428
+ (filterChange)="applyFilter($event)"
429
+ ></jqt-slice-panel>
430
+ ```
431
+
432
+ ### Inputs `SlicePanelComponent`
433
+
434
+ | Propriété | Type | Description |
435
+ |-----------|------|-------------|
436
+ | `sliceConfigs` | `SliceConfig<T>[]` | Configurations des slices |
437
+ | `columns` | `SliceColumnDef<T>[]` | Définitions des colonnes pour la résolution des valeurs |
438
+ | `data` | `T[]` | Données source |
439
+ | `lazyData` | `Map<string, Map<any,any>>` | Valeurs lazy chargées |
440
+ | `lazyStatus` | `Map<string, 'idle'\|'loading'\|'loaded'\|'error'>` | État par colonne lazy |
441
+ | `showToggle` | `boolean` | Affiche le bouton collapse du panel (défaut : `true`) |
442
+ | `showCounts` | `boolean` | Affiche le nombre de lignes par catégorie (défaut : `true`) |
443
+ | `collapsedByDefault` | `boolean` | Les slices démarrent repliés (défaut : `false`) |
444
+ | `alwaysShow` | `boolean` | Affiche le panel même si vide (défaut : `false`) |
445
+
446
+ ### Outputs `SlicePanelComponent`
447
+
448
+ | Événement | Payload | Description |
449
+ |-----------|---------|-------------|
450
+ | `filterChange` | `(row: T) => boolean` | Prédicat de filtre à appliquer |
451
+ | `activeKeysChange` | `string[][]` | Clés actives par slice |
452
+ | `dynamicSliceKeysChange` | `string[]` | Clés des slices dynamiques actives |
453
+ | `collapsedChange` | `boolean` | État replié/déplié du panel |
454
+
455
+ ---
456
+
457
+ ## Personnalisation visuelle — CSS variables
149
458
 
150
459
  ```scss
151
460
  jquery-table {
152
461
  /* Texte */
153
- --jqt-title-color: #1f2937; /* titre du tableau (h3) */
154
- --jqt-text-color: #111827; /* texte principal (cellules, labels) */
155
- --jqt-text-secondary: #6b7280; /* texte secondaire (badges, infos) */
156
- --jqt-text-muted: #9ca3af; /* placeholder, icônes discrètes */
462
+ --jqt-title-color: #1f2937;
463
+ --jqt-text-color: #111827;
464
+ --jqt-text-secondary: #6b7280;
465
+ --jqt-text-muted: #9ca3af;
157
466
 
158
467
  /* Surfaces */
159
- --jqt-surface-color: #ffffff; /* fond des éléments (barre de recherche…) */
160
- --jqt-surface-alt-color: #f9fafb; /* fond alternatif subtil */
161
- --jqt-row-hover-bg: #f3f4f6; /* survol d'une ligne / fond badge */
468
+ --jqt-surface-color: #ffffff;
469
+ --jqt-surface-alt-color: #f9fafb;
470
+ --jqt-row-hover-bg: #f3f4f6;
162
471
 
163
472
  /* Bordures */
164
- --jqt-border-color: #e5e7eb; /* bordures du tableau et éléments UI */
473
+ --jqt-border-color: #e5e7eb;
165
474
 
166
- /* Groupes (groupBy) */
167
- --jqt-group-bg: #eef2f7; /* fond des lignes de groupe */
168
- --jqt-group-bg-hover: #e4ecf4; /* fond au survol d'une ligne de groupe */
169
- --jqt-group-accent: #94a3b8; /* bande colorée gauche des groupes */
170
- --jqt-group-border: #d1d9e4; /* bordures haut/bas des groupes */
475
+ /* Group by */
476
+ --jqt-group-bg: #eef2f7;
477
+ --jqt-group-bg-hover: #e4ecf4;
478
+ --jqt-group-accent: #94a3b8;
479
+ --jqt-group-border: #d1d9e4;
171
480
 
172
481
  /* Accent & danger */
173
- --jqt-primary-color: #1d4ed8; /* couleur d'accent (focus, sélection) */
174
- --jqt-danger-color: #dc2626; /* bouton reset (survol) */
482
+ --jqt-primary-color: #1d4ed8;
483
+ --jqt-danger-color: #dc2626;
175
484
  }
176
485
  ```
177
486
 
178
- **Exemple** — thème violet :
487
+ ---
179
488
 
180
- ```scss
181
- jquery-table {
182
- --jqt-primary-color: #7c3aed;
183
- --jqt-group-accent: #7c3aed;
184
- --jqt-group-bg: #f5f3ff;
185
- --jqt-group-border: #ddd6fe;
186
- }
489
+ ## Utilitaires exportés
490
+
491
+ ```ts
492
+ import { normalizeCellValue, humanizeKey, getFrenchPaginatorIntl } from '@oneteme/jquery-table';
187
493
  ```
188
494
 
495
+ | Fonction | Description |
496
+ |----------|-------------|
497
+ | `normalizeCellValue(value)` | Convertit n'importe quelle valeur de cellule en string affichable (gère `null`, `Date`, `Array`, objet `{label}` / `{name}`) |
498
+ | `humanizeKey(key)` | Transforme `camelCase` / `snake_case` en label lisible (`createdAt` → `"Created at"`) |
499
+ | `col(key, header, overrides?)` | Raccourci pour créer un `TableColumnProvider` en une ligne |
500
+ | `getFrenchPaginatorIntl()` | Retourne un `MatPaginatorIntl` avec les labels en français |
501
+
502
+ > **`normalizeCellValue`** utilise la locale navigateur (et non fr-FR) pour les dates.
503
+
504
+ ---
505
+
189
506
  ## Build
190
507
 
191
508
  ```bash