@cityway/bo-ui 1.1.0-beta010 → 1.1.0-beta013
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/cityway-bo-ui-1.1.0-beta013.tgz +0 -0
- package/docs/components/drag-n-drop-nested-list.md +14 -8
- package/docs/components/import-wizard.md +22 -2
- package/fesm2022/cityway-bo-ui.mjs +161 -31
- package/fesm2022/cityway-bo-ui.mjs.map +1 -1
- package/index.d.ts +89 -15
- package/lib/assets/i18n/common.en.json +40 -25
- package/lib/assets/i18n/common.fr.json +40 -33
- package/package.json +2 -2
- package/styles/bo-ui.styles.min.css +1 -1
- package/styles/bo-ui.styles.min.css.map +1 -1
- package/cityway-bo-ui-1.1.0-beta009.tgz +0 -0
- package/cityway-bo-ui-1.1.0-beta010.tgz +0 -0
|
Binary file
|
|
@@ -29,11 +29,11 @@ Liste hiérarchique réordonnable par glisser-déposer (CDK drag-drop), pilotée
|
|
|
29
29
|
| Input | Type | Défaut | Description |
|
|
30
30
|
|-------|------|--------|-------------|
|
|
31
31
|
| `items` | `DragItem[]` | `[]` | Arbre des items à afficher. Le composant en fait une **copie interne mutable** sur laquelle le CDK opère. Voir §3. |
|
|
32
|
-
| `itemTemplate` | `TemplateRef<any> \| undefined` | `undefined` | Template du **corps** de carte (slot). Reçoit `$implicit = item` et `depth`. Si absent,
|
|
32
|
+
| `itemTemplate` | `TemplateRef<any> \| undefined` | `undefined` | Template du **corps** de carte (slot). Reçoit `$implicit = item` et `depth`. Si absent, **aucun corps n'est rendu** — sauf `item.code`, affiché seul (le titre de l'en-tête porte déjà `title ?? label`). |
|
|
33
33
|
| `column` | `DragListColumn \| undefined` | `undefined` | Configuration de la colonne : titre, défauts de visibilité des actions, libellés, plafonds. Voir §3. |
|
|
34
34
|
| `reorderOnDrop` | `boolean` | `true` | Renumérote `order` (1..n par niveau) récursivement à chaque drop. `false` = déplacement visuel seul, `order` intact. |
|
|
35
35
|
| `maxDepth` | `number \| undefined` | `undefined` | Profondeur max (0-based, racine = 0). Une carte à `depth < maxDepth` peut recevoir des enfants ; `>= maxDepth` est terminale. `undefined` = imbrication infinie. Ex. `maxDepth = 1` → 2 niveaux. |
|
|
36
|
-
| `selectedId` | `number \| string \| undefined` | `undefined` | Sélection **exclusive** gérée par la lib : la carte dont `id === selectedId` reçoit la classe `ddn-list-card-selected
|
|
36
|
+
| `selectedId` | `number \| string \| undefined` | `undefined` | Sélection **exclusive** gérée par la lib : la carte dont `id === selectedId` reçoit la classe `ddn-list-card-selected` — **elle seule**, jamais ses parents (voir §5, « Rendu de la sélection en profondeur »). Le flag `DragItem.selected` reste supporté en parallèle. ⚠️ Les `id` doivent être uniques **sur tout l'arbre**, pas seulement par niveau. |
|
|
37
37
|
|
|
38
38
|
### Outputs
|
|
39
39
|
|
|
@@ -60,6 +60,8 @@ Le **corps** de chaque carte est fourni par un `TemplateRef` passé à `[itemTem
|
|
|
60
60
|
|
|
61
61
|
> L'en-tête (titre, sous-titre, badge, compteur, actions, poignée) reste **toujours** data-driven ; seul le corps est personnalisable.
|
|
62
62
|
|
|
63
|
+
Sans `[itemTemplate]`, la carte se réduit à son en-tête : le composant ne réaffiche pas le label, qui est déjà le titre. Seul `item.code`, s'il est renseigné, est rendu dans le corps. C'est le mode à privilégier quand l'en-tête suffit — chaque corps redondant coûte une hauteur de carte à tous les niveaux de l'arbre.
|
|
64
|
+
|
|
63
65
|
---
|
|
64
66
|
|
|
65
67
|
## 3. Modèles & types
|
|
@@ -67,8 +69,8 @@ Le **corps** de chaque carte est fourni par un `TemplateRef` passé à `[itemTem
|
|
|
67
69
|
```ts
|
|
68
70
|
interface DragItem {
|
|
69
71
|
id: number | string; // clé de tri (trackBy) — unique
|
|
70
|
-
label: string; // libellé (fallback de title
|
|
71
|
-
code?: string; // affiché dans le corps
|
|
72
|
+
label: string; // libellé (fallback de title dans l'en-tête)
|
|
73
|
+
code?: string; // affiché seul dans le corps quand il n'y a pas d'itemTemplate
|
|
72
74
|
order?: number; // position 1..n (renumérotée si reorderOnDrop)
|
|
73
75
|
disabled?: boolean; // verrouille le drag de CETTE carte (poignée inactive)
|
|
74
76
|
children?: DragItem[]; // sous-arbre
|
|
@@ -101,8 +103,8 @@ interface DragItemActions { // chaque champ = visibilité (booléen). unde
|
|
|
101
103
|
|
|
102
104
|
interface DragListColumn {
|
|
103
105
|
title: string; // titre de colonne (clé i18n) — affiché « Titre (count) » / « (count/max) »
|
|
104
|
-
description?: string;
|
|
105
|
-
descriptionRwd?: string;
|
|
106
|
+
description?: string; // IGNORÉ par ce composant (n'existe que pour cw-drag-n-drop-list)
|
|
107
|
+
descriptionRwd?: string; // idem
|
|
106
108
|
cssClass?: string; // classe sur chaque <ul> de niveau
|
|
107
109
|
|
|
108
110
|
collapsible?: boolean; // cartes pliables (défaut true) ; false désactive globalement
|
|
@@ -256,6 +258,10 @@ restore(t: DragItem) {
|
|
|
256
258
|
onReorder(items: DragItem[]) { this.items = items; }
|
|
257
259
|
```
|
|
258
260
|
|
|
261
|
+
**Rendu en profondeur.** Une seule carte est peinte à l'écran, jamais toute une branche. La carte sélectionnée porte `ddn-list-card-selected` ; un parent qui la contient ne porte `ddn-list-card-has-selection` que **s'il est replié** — la carte sélectionnée n'étant alors pas rendue (`destroyOnHide` de `cw-card`), le parent en reprend le rendu et signale « un élément actif est là-dedans ». Déplié, il redevient neutre. La lib suit le pliage réel de chaque carte via `(collapseClicked)`, pas seulement l'état initial.
|
|
262
|
+
|
|
263
|
+
⚠️ **Unicité des `id`.** La correspondance se fait sur `id === selectedId` à tous les niveaux. Si un item de niveau 1 et un item de niveau 2 partagent le même `id` (typique quand chaque niveau vient d'une table différente : thème `1`, fichier `1`), **les deux cartes s'allument**. Préfixer côté hôte (`` `theme-${t.id}` ``, `` `file-${f.id}` ``) ou utiliser une clé composite.
|
|
264
|
+
|
|
259
265
|
### Corps de carte custom (`itemTemplate`)
|
|
260
266
|
|
|
261
267
|
```html
|
|
@@ -272,12 +278,12 @@ onReorder(items: DragItem[]) { this.items = items; }
|
|
|
272
278
|
|
|
273
279
|
## 5. Notes d'intégration
|
|
274
280
|
|
|
275
|
-
- **CDK drag-drop** : repose sur `@angular/cdk/drag-drop` (`DragDropModule`). Chaque niveau est un `cdkDropList` **isolé** (pas de `cdkDropListConnectedTo`) → le tri est **intra-liste à chaque niveau**, jamais entre niveaux ni entre listes. Le drag ne démarre que depuis la **poignée** (`cdkDragHandle`).
|
|
281
|
+
- **CDK drag-drop** : repose sur `@angular/cdk/drag-drop` (`DragDropModule`). Chaque niveau est un `cdkDropList` **isolé** (pas de `cdkDropListConnectedTo`) → le tri est **intra-liste à chaque niveau**, jamais entre niveaux ni entre listes. Le drag ne démarre que depuis la **poignée** (`cdkDragHandle`). Poignée inactive (carte `disabled` ou `pendingDelete`) : elle garde son rendu normal, seule l'interaction disparaît — le rendu `disabled` du `.btn` (aplat plein `base-disabled`) est neutralisé sur `.ddn-list-handle`, sinon une poignée morte est plus voyante qu'une poignée active.
|
|
276
282
|
- **Source de vérité** : le composant clone `items` dans une copie interne mutable (`_items`) à chaque changement (effect). C'est cette copie que le CDK déplace ; l'arbre réordonné est émis via `itemsChange`. **Persister** ce payload côté hôte (sinon un re-rendu ultérieur repart de l'ordre périmé).
|
|
277
283
|
- **Imbrication & `maxDepth`** : `maxDepth` est 0-based (racine = 0). `maxDepth = 1` ⇒ 2 niveaux. `undefined` ⇒ imbrication infinie (ajout possible partout).
|
|
278
284
|
- **Type de carte alterné** : la lib alterne `CardTypeEnum.default` (profondeur paire) / `light` (impaire).
|
|
279
285
|
- **i18n** : tous les libellés (`column.title`, `addLabel`, `maxReachedLabel`, `restoreBlockedLabel`, libellés de badge…) passent par `translate` → fournir des **clés**, pas du texte en dur.
|
|
280
|
-
- **États & styles** : la lib **lit** `selected` / `selectedId` / `pendingDelete` et pose les classes DS (`ddn-list-card-selected`, `ddn-list-card-deleting`) ; elle ne mute pas les flags. C'est l'hôte qui les flippe et re-passe `items`. Préférer `selectedId` (exclusif, sans parcours d'arbre) au flag `selected` (multi-sélection non garantie).
|
|
286
|
+
- **États & styles** : la lib **lit** `selected` / `selectedId` / `pendingDelete` et pose les classes DS (`ddn-list-card-selected`, `ddn-list-card-has-selection`, `ddn-list-card-deleting`) ; elle ne mute pas les flags. C'est l'hôte qui les flippe et re-passe `items`. Préférer `selectedId` (exclusif, sans parcours d'arbre) au flag `selected` (multi-sélection non garantie).
|
|
281
287
|
- **Soft-delete** : sur une carte `pendingDelete`, le corps est masqué (carte compacte, non pliable, non déplaçable) et la corbeille devient « Restaurer » (émet `restore`, pas `delete`). En mode `excludePendingDeleteFromMax`, restaurer peut être **bloqué** si le niveau est entre-temps redevenu plein.
|
|
282
288
|
|
|
283
289
|
---
|
|
@@ -109,7 +109,7 @@ interface ImportWizardConfig {
|
|
|
109
109
|
modes: readonly [ImportModeOption, ...ImportModeOption[]]; // jamais vide ; 1 élément = imposé
|
|
110
110
|
interruption: ImportInterruption; // requis : ce qui se passe vraiment à l'arrêt
|
|
111
111
|
showErrorDetails?: boolean; // [Option]
|
|
112
|
-
scope
|
|
112
|
+
scope: ImportScope; // requis — voir ci-dessous
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
@@ -139,6 +139,26 @@ Aucune de ces situations n'est rattrapée à l'exécution : elles ne compilent p
|
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
142
|
+
### `scope` — et le nom de l'objet importé
|
|
143
|
+
|
|
144
|
+
`scope` est un type discriminé : le libellé de l'objet **voyage avec la portée qui l'affiche**, comme `keyFieldHint` voyage avec son mode. Indéclarable en `singleConfig` (aucun compteur n'y est rendu), inoubliable en `list`.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
scope: { kind: ImportScopeEnum.list, objectLabel: i18nKey('DOMAIN.USER') }
|
|
148
|
+
// ou
|
|
149
|
+
scope: { kind: ImportScopeEnum.singleConfig }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`objectLabel` est une clé **racine**, suffixée par le composant de la catégorie de pluriel de la langue active — exactement comme les libellés de compteur. Le bundle de l'application doit donc déclarer les trois catégories :
|
|
153
|
+
|
|
154
|
+
```jsonc
|
|
155
|
+
"DOMAIN": { "USER": { "one": "utilisateur", "many": "utilisateurs", "other": "utilisateurs" } }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Rendu : « **2 431** utilisateurs à ajouter ».
|
|
159
|
+
|
|
160
|
+
> ⚠️ **Accord.** Les libellés du *rapport* sont des participes passés accordés au masculin pluriel (« … ajoutés », « … supprimés »). Un nom féminin (« lignes ») donnerait « lignes ajoutés ». Le cas échéant, surcharger `_COMMON._IMPORT._COUNTS.REPORT_*` dans le bundle de l'application — le deepmerge du loader donne le dernier mot à l'application.
|
|
161
|
+
|
|
142
162
|
## 4. Exemples
|
|
143
163
|
|
|
144
164
|
### Import d'une liste, ouvert depuis le bouton *Importer* de l'en-tête
|
|
@@ -270,7 +290,7 @@ readonly configImport: ImportWizardConfig = {
|
|
|
270
290
|
accept: '.json',
|
|
271
291
|
acceptLabel: 'JSON',
|
|
272
292
|
modes: [{ mode: ImportModeEnum.replaceAll }], // un seul mode => imposé, lecture seule
|
|
273
|
-
scope: ImportScopeEnum.singleConfig,
|
|
293
|
+
scope: { kind: ImportScopeEnum.singleConfig }, // pas de compteurs, message de conformité
|
|
274
294
|
interruption: { kind: ImportInterruptionEnum.atomic }, // back-end transactionnel
|
|
275
295
|
source: { analyze: …, execute: … },
|
|
276
296
|
};
|