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,136 +1,154 @@
|
||||
<aside class="secondary-sidebar"
|
||||
[class.collapsed]="isCollapsed"
|
||||
[style.width.px]="isCollapsed ? null : width">
|
||||
[class.collapsed]="isCollapsed"
|
||||
[style.width.px]="isCollapsed ? null : width">
|
||||
|
||||
<div class="collapse-toggle" (click)="toggleCollapse()">
|
||||
<lucide-icon [img]="isCollapsed ? PanelLeftOpen : PanelLeftClose" [size]="16"></lucide-icon>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!isCollapsed">
|
||||
|
||||
<button
|
||||
*ngIf="titleRoute; else staticTitle"
|
||||
type="button"
|
||||
class="sidebar-title sidebar-title--link"
|
||||
[class.active]="isTitleActive()"
|
||||
title="Retour à l'accueil de la campagne"
|
||||
(click)="clickTitle()">
|
||||
<lucide-icon [img]="Home" [size]="14" class="sidebar-title-icon"></lucide-icon>
|
||||
<span class="sidebar-title-text">{{ title }}</span>
|
||||
</button>
|
||||
<ng-template #staticTitle>
|
||||
<h2 class="sidebar-title">{{ title }}</h2>
|
||||
</ng-template>
|
||||
|
||||
<div class="actions-row" *ngIf="createActions.length">
|
||||
@if (!isCollapsed) {
|
||||
@if (titleRoute) {
|
||||
<button
|
||||
*ngFor="let action of createActions"
|
||||
class="btn-pill"
|
||||
[class.primary]="action.variant === 'primary'"
|
||||
[class.secondary]="action.variant === 'secondary'"
|
||||
(click)="runAction(action)">
|
||||
{{ action.label }}
|
||||
type="button"
|
||||
class="sidebar-title sidebar-title--link"
|
||||
[class.active]="isTitleActive()"
|
||||
title="Retour à l'accueil de la campagne"
|
||||
(click)="clickTitle()">
|
||||
<lucide-icon [img]="Home" [size]="14" class="sidebar-title-icon"></lucide-icon>
|
||||
<span class="sidebar-title-text">{{ title }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
} @else {
|
||||
<h2 class="sidebar-title">{{ title }}</h2>
|
||||
}
|
||||
@if (createActions.length) {
|
||||
<div class="actions-row">
|
||||
@for (action of createActions; track action) {
|
||||
<button
|
||||
class="btn-pill"
|
||||
[class.primary]="action.variant === 'primary'"
|
||||
[class.secondary]="action.variant === 'secondary'"
|
||||
(click)="runAction(action)">
|
||||
{{ action.label }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="tree">
|
||||
<ng-container *ngFor="let item of items">
|
||||
@for (item of items; track item) {
|
||||
<ng-container *ngTemplateOutlet="treeNode; context: { $implicit: item, level: 0 }"></ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Template récursif : un noeud d'arbre rend son bouton, puis ses enfants via ce même template -->
|
||||
<ng-template #treeNode let-item let-level="level">
|
||||
<div class="tree-section-header" *ngIf="level === 0 && item.sectionHeaderBefore">
|
||||
{{ item.sectionHeaderBefore }}
|
||||
</div>
|
||||
@if (level === 0 && item.sectionHeaderBefore) {
|
||||
<div class="tree-section-header">
|
||||
{{ item.sectionHeaderBefore }}
|
||||
</div>
|
||||
}
|
||||
<div class="tree-item" [style.padding-left.px]="level * 12">
|
||||
<div class="tree-row">
|
||||
<button
|
||||
*ngIf="!item.isAction && isExpandable(item)"
|
||||
type="button"
|
||||
class="chevron-btn"
|
||||
(click)="clickChevron($event, item)">
|
||||
<lucide-icon
|
||||
[img]="isExpanded(item.id) ? ChevronDown : ChevronRight"
|
||||
[size]="12">
|
||||
</lucide-icon>
|
||||
</button>
|
||||
<span *ngIf="item.isAction || !isExpandable(item)" class="chevron-spacer"></span>
|
||||
|
||||
<button type="button" class="tree-btn"
|
||||
[class.action]="item.isAction"
|
||||
[class.active]="isActive(item)"
|
||||
(click)="clickItem(item)">
|
||||
<lucide-icon
|
||||
*ngIf="iconFor(item) as icon"
|
||||
[img]="icon"
|
||||
[size]="14"
|
||||
class="item-icon">
|
||||
</lucide-icon>
|
||||
{{ item.label }}
|
||||
<span class="tree-item-meta" *ngIf="!item.isAction && item.meta">{{ item.meta }}</span>
|
||||
</button>
|
||||
|
||||
<!-- Actions de creation contextuelles, revelees au survol de la ligne -->
|
||||
<span class="node-actions" *ngIf="item.createActions?.length">
|
||||
@if (!item.isAction && isExpandable(item)) {
|
||||
<button
|
||||
*ngFor="let a of item.createActions"
|
||||
type="button"
|
||||
class="node-action-btn"
|
||||
[title]="a.label"
|
||||
[attr.aria-label]="a.label"
|
||||
(click)="runCreateAction($event, a)">
|
||||
<lucide-icon [img]="iconForAction(a)" [size]="16"></lucide-icon>
|
||||
class="chevron-btn"
|
||||
(click)="clickChevron($event, item)">
|
||||
<lucide-icon
|
||||
[img]="isExpanded(item.id) ? ChevronDown : ChevronRight"
|
||||
[size]="12">
|
||||
</lucide-icon>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="tree-children" *ngIf="isExpanded(item.id) && hasChildren(item)">
|
||||
<ng-container *ngFor="let child of item.children">
|
||||
<ng-container *ngTemplateOutlet="treeNode; context: { $implicit: child, level: level + 1 }"></ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
@if (item.isAction || !isExpandable(item)) {
|
||||
<span class="chevron-spacer"></span>
|
||||
}
|
||||
<button type="button" class="tree-btn"
|
||||
[class.action]="item.isAction"
|
||||
[class.active]="isActive(item)"
|
||||
(click)="clickItem(item)">
|
||||
@if (iconFor(item); as icon) {
|
||||
<lucide-icon
|
||||
[img]="icon"
|
||||
[size]="14"
|
||||
class="item-icon">
|
||||
</lucide-icon>
|
||||
}
|
||||
{{ item.label }}
|
||||
@if (!item.isAction && item.meta) {
|
||||
<span class="tree-item-meta">{{ item.meta }}</span>
|
||||
}
|
||||
</button>
|
||||
<!-- Actions de creation contextuelles, revelees au survol de la ligne -->
|
||||
@if (item.createActions?.length) {
|
||||
<span class="node-actions">
|
||||
@for (a of item.createActions; track a) {
|
||||
<button
|
||||
type="button"
|
||||
class="node-action-btn"
|
||||
[title]="a.label"
|
||||
[attr.aria-label]="a.label"
|
||||
(click)="runCreateAction($event, a)">
|
||||
<lucide-icon [img]="iconForAction(a)" [size]="16"></lucide-icon>
|
||||
</button>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
@if (isExpanded(item.id) && hasChildren(item)) {
|
||||
<div class="tree-children">
|
||||
@for (child of item.children; track child) {
|
||||
<ng-container *ngTemplateOutlet="treeNode; context: { $implicit: child, level: level + 1 }"></ng-container>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- Panneau bas (ex: Templates) ------------------------------------ -->
|
||||
<section class="bottom-panel" *ngIf="bottomPanel">
|
||||
<div class="panel-header-row">
|
||||
<button class="panel-header" (click)="togglePanel()">
|
||||
<span class="panel-title">{{ bottomPanel.title }}</span>
|
||||
<lucide-icon
|
||||
[img]="panelOpen ? ChevronDown : ChevronRight"
|
||||
[size]="14">
|
||||
</lucide-icon>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="bottomPanel.headerAction as action"
|
||||
type="button"
|
||||
class="panel-header-action"
|
||||
[title]="action.label"
|
||||
[attr.aria-label]="action.label"
|
||||
(click)="runPanelHeaderAction($event, action)">
|
||||
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="panel-list" *ngIf="panelOpen">
|
||||
<li *ngFor="let item of bottomPanel.items">
|
||||
<button
|
||||
class="panel-item"
|
||||
[class.action]="item.isAction"
|
||||
(click)="clickPanelItem(item)">
|
||||
<span class="panel-item-label">{{ item.label }}</span>
|
||||
<span class="panel-item-meta" *ngIf="item.meta">{{ item.meta }}</span>
|
||||
@if (bottomPanel) {
|
||||
<section class="bottom-panel">
|
||||
<div class="panel-header-row">
|
||||
<button class="panel-header" (click)="togglePanel()">
|
||||
<span class="panel-title">{{ bottomPanel.title }}</span>
|
||||
<lucide-icon
|
||||
[img]="panelOpen ? ChevronDown : ChevronRight"
|
||||
[size]="14">
|
||||
</lucide-icon>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</ng-container>
|
||||
@if (bottomPanel.headerAction; as action) {
|
||||
<button
|
||||
type="button"
|
||||
class="panel-header-action"
|
||||
[title]="action.label"
|
||||
[attr.aria-label]="action.label"
|
||||
(click)="runPanelHeaderAction($event, action)">
|
||||
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
@if (panelOpen) {
|
||||
<ul class="panel-list">
|
||||
@for (item of bottomPanel.items; track item) {
|
||||
<li>
|
||||
<button
|
||||
class="panel-item"
|
||||
[class.action]="item.isAction"
|
||||
(click)="clickPanelItem(item)">
|
||||
<span class="panel-item-label">{{ item.label }}</span>
|
||||
@if (item.meta) {
|
||||
<span class="panel-item-meta">{{ item.meta }}</span>
|
||||
}
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Poignée de redimensionnement sur le bord droit (masquée si replié) -->
|
||||
<div class="resize-handle"
|
||||
*ngIf="!isCollapsed"
|
||||
(mousedown)="startResize($event)"
|
||||
title="Glissez pour redimensionner"></div>
|
||||
@if (!isCollapsed) {
|
||||
<div class="resize-handle"
|
||||
(mousedown)="startResize($event)"
|
||||
title="Glissez pour redimensionner"></div>
|
||||
}
|
||||
</aside>
|
||||
|
||||
Reference in New Issue
Block a user