Migration Angular 20 -> 21 : fin de la migration securite (0 vulnerabilite npm)
- ng update @angular/core@21 @angular/cli@21 (corrige GHSA-jrmj-c5cx-3cw6, XSS SVG) - Migration automatique des templates vers le control flow natif (@if/@for, 138 fichiers) - Correction des 5 `track` invalides generes par la migration (trackBy a 1 argument -> track $index) + suppression des fonctions trackBy mortes - TypeScript 5.9, zone.js 0.15 ; npm audit : 0 vulnerabilite Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,60 +1,66 @@
|
||||
<div class="prereq-editor">
|
||||
|
||||
<p class="prereq-editor-empty" *ngIf="prerequisites.length === 0">
|
||||
Aucune condition — la quête est immédiatement disponible.
|
||||
</p>
|
||||
@if (prerequisites.length === 0) {
|
||||
<p class="prereq-editor-empty">
|
||||
Aucune condition — la quête est immédiatement disponible.
|
||||
</p>
|
||||
}
|
||||
|
||||
<ul class="prereq-list" *ngIf="prerequisites.length > 0">
|
||||
<li class="prereq-row" *ngFor="let p of prerequisites; let i = index; trackBy: trackByIndex">
|
||||
|
||||
<ng-container [ngSwitch]="p.kind">
|
||||
|
||||
<!-- QUEST_COMPLETED -->
|
||||
<ng-container *ngSwitchCase="'QUEST_COMPLETED'">
|
||||
<span class="prereq-row-label">Quête terminée :</span>
|
||||
<select class="prereq-input"
|
||||
[ngModel]="asQuestCompleted(p).questId"
|
||||
(ngModelChange)="onQuestIdChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[disabled]="availableQuests.length === 0">
|
||||
<option *ngIf="availableQuests.length === 0" value="">(aucune autre quête)</option>
|
||||
<option *ngFor="let q of availableQuests" [value]="q.id">{{ q.name }}</option>
|
||||
</select>
|
||||
</ng-container>
|
||||
|
||||
<!-- SESSION_REACHED -->
|
||||
<ng-container *ngSwitchCase="'SESSION_REACHED'">
|
||||
<span class="prereq-row-label">À partir de la session :</span>
|
||||
<input type="number"
|
||||
class="prereq-input prereq-input-number"
|
||||
min="1"
|
||||
[ngModel]="asSessionReached(p).minSessionNumber"
|
||||
(ngModelChange)="onMinSessionChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }" />
|
||||
</ng-container>
|
||||
|
||||
<!-- FLAG_SET -->
|
||||
<ng-container *ngSwitchCase="'FLAG_SET'">
|
||||
<span class="prereq-row-label">Quand le fait est vrai :</span>
|
||||
<input type="text"
|
||||
class="prereq-input"
|
||||
placeholder="nom_du_fait"
|
||||
[attr.list]="'prereq-flag-list-' + i"
|
||||
[ngModel]="asFlagSet(p).flagName"
|
||||
(ngModelChange)="onFlagNameChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }" />
|
||||
<datalist [attr.id]="'prereq-flag-list-' + i">
|
||||
<option *ngFor="let f of availableFlags" [value]="f"></option>
|
||||
</datalist>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<button type="button" class="prereq-remove" (click)="removeAt(i)" title="Retirer ce prérequis">
|
||||
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@if (prerequisites.length > 0) {
|
||||
<ul class="prereq-list">
|
||||
@for (p of prerequisites; track $index; let i = $index) {
|
||||
<li class="prereq-row">
|
||||
@switch (p.kind) {
|
||||
<!-- QUEST_COMPLETED -->
|
||||
@case ('QUEST_COMPLETED') {
|
||||
<span class="prereq-row-label">Quête terminée :</span>
|
||||
<select class="prereq-input"
|
||||
[ngModel]="asQuestCompleted(p).questId"
|
||||
(ngModelChange)="onQuestIdChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[disabled]="availableQuests.length === 0">
|
||||
@if (availableQuests.length === 0) {
|
||||
<option value="">(aucune autre quête)</option>
|
||||
}
|
||||
@for (q of availableQuests; track q) {
|
||||
<option [value]="q.id">{{ q.name }}</option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
<!-- SESSION_REACHED -->
|
||||
@case ('SESSION_REACHED') {
|
||||
<span class="prereq-row-label">À partir de la session :</span>
|
||||
<input type="number"
|
||||
class="prereq-input prereq-input-number"
|
||||
min="1"
|
||||
[ngModel]="asSessionReached(p).minSessionNumber"
|
||||
(ngModelChange)="onMinSessionChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }" />
|
||||
}
|
||||
<!-- FLAG_SET -->
|
||||
@case ('FLAG_SET') {
|
||||
<span class="prereq-row-label">Quand le fait est vrai :</span>
|
||||
<input type="text"
|
||||
class="prereq-input"
|
||||
placeholder="nom_du_fait"
|
||||
[attr.list]="'prereq-flag-list-' + i"
|
||||
[ngModel]="asFlagSet(p).flagName"
|
||||
(ngModelChange)="onFlagNameChange(i, $event)"
|
||||
[ngModelOptions]="{ standalone: true }" />
|
||||
<datalist [attr.id]="'prereq-flag-list-' + i">
|
||||
@for (f of availableFlags; track f) {
|
||||
<option [value]="f"></option>
|
||||
}
|
||||
</datalist>
|
||||
}
|
||||
}
|
||||
<button type="button" class="prereq-remove" (click)="removeAt(i)" title="Retirer ce prérequis">
|
||||
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<div class="prereq-add">
|
||||
<button type="button" class="btn-secondary prereq-add-btn" (click)="toggleAddMenu()">
|
||||
@@ -63,17 +69,19 @@
|
||||
<lucide-icon [img]="ChevronDown" [size]="14"></lucide-icon>
|
||||
</button>
|
||||
|
||||
<div class="prereq-add-menu" *ngIf="addMenuOpen">
|
||||
<button type="button" (click)="addPrerequisite('QUEST_COMPLETED')">
|
||||
Après une autre quête
|
||||
</button>
|
||||
<button type="button" (click)="addPrerequisite('SESSION_REACHED')">
|
||||
À partir d'une session
|
||||
</button>
|
||||
<button type="button" (click)="addPrerequisite('FLAG_SET')">
|
||||
Quand un fait est vrai
|
||||
</button>
|
||||
</div>
|
||||
@if (addMenuOpen) {
|
||||
<div class="prereq-add-menu">
|
||||
<button type="button" (click)="addPrerequisite('QUEST_COMPLETED')">
|
||||
Après une autre quête
|
||||
</button>
|
||||
<button type="button" (click)="addPrerequisite('SESSION_REACHED')">
|
||||
À partir d'une session
|
||||
</button>
|
||||
<button type="button" (click)="addPrerequisite('FLAG_SET')">
|
||||
Quand un fait est vrai
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LucideAngularModule, Plus, Trash2, ChevronDown } from 'lucide-angular';
|
||||
import { Chapter, Prerequisite } from '../../services/campaign.model';
|
||||
@@ -13,7 +13,7 @@ import { Chapter, Prerequisite } from '../../services/campaign.model';
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-prerequisite-editor',
|
||||
imports: [CommonModule, FormsModule, LucideAngularModule],
|
||||
imports: [FormsModule, LucideAngularModule],
|
||||
templateUrl: './prerequisite-editor.component.html',
|
||||
styleUrls: ['./prerequisite-editor.component.scss']
|
||||
})
|
||||
@@ -81,7 +81,6 @@ export class PrerequisiteEditorComponent {
|
||||
}
|
||||
|
||||
/** trackBy stable pour *ngFor (sinon les inputs reset à chaque édition). */
|
||||
trackByIndex(i: number): number { return i; }
|
||||
|
||||
// === Casts typés pour le template ===
|
||||
// Angular strict templates ne fait pas le narrowing d'une union discriminée à travers
|
||||
|
||||
Reference in New Issue
Block a user