Files
LoreMind/web/src/app/services/layout.service.ts
IETM_FIXE\ietm6 6d00543a59
Some checks failed
Build & Push Images / build (web) (push) Has been cancelled
Build & Push Images / build-switcher (push) Has been cancelled
Build & Push Images / build (core) (push) Has been cancelled
Build & Push Images / build (brain) (push) Has been cancelled
Ajout de 2 fonctionnalitées principales : import PDF que ce soit pour les règles ou les campagnes directement.
Fonctionnalité de comparaison PDF / campagne pour faire un mix et demander des conseils à l'IA
2026-06-04 13:55:27 +02:00

130 lines
4.1 KiB
TypeScript

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
export interface TreeItem {
id: string;
label: string;
children?: TreeItem[];
route?: string; // si défini, cliquer navigue au lieu de toggler
isAction?: boolean; // style "action" (ex: "+ Nouveau chapitre")
/** Clé d'icône optionnelle (ex: "users"). Résolue par le composant via `resolveIcon`. */
iconKey?: string;
/** Petit badge affiché à droite (ex: "3" pour compter les pages d'un dossier). */
meta?: string;
/**
* Libellé de section affiché AU-DESSUS du nœud, avec un filet de séparation.
* Utilisé pour grouper visuellement des nœuds racines (ex: "Personnages" vs "Narration").
*/
sectionHeaderBefore?: string;
/**
* Actions de creation contextuelles (ex: "+ Nouveau chapitre" sur un arc).
* Affichees comme boutons icone au survol du noeud (repli visuel), et en
* pleine largeur si le noeud est expanded sans aucun enfant reel
* (empty-state inline, meilleur des deux mondes).
*/
createActions?: TreeCreateAction[];
}
export interface TreeCreateAction {
id: string;
label: string; // tooltip au hover, texte complet en empty-state
route: string;
/** Cle d'icone cote sidebar (plus | folder-plus | file-plus). */
actionIcon?: 'plus' | 'folder-plus' | 'file-plus';
}
export interface GlobalItem {
id: string;
name: string;
route: string;
}
export interface SidebarAction {
id: string;
label: string;
variant: 'primary' | 'secondary' | 'success';
route?: string;
}
/**
* Item affiché dans un panneau secondaire (ex: liste des Templates).
* Plus simple qu'un TreeItem : pas de récursion, juste label + meta + route.
*/
export interface BottomPanelItem {
id: string;
label: string;
meta?: string; // petit badge à droite (ex: "8 champs")
route?: string;
isAction?: boolean; // style "action" (ex: "+ Nouveau template")
}
/**
* Panneau secondaire collapsible en bas de la sidebar (sous l'arbre).
* Utilisé notamment pour le panneau "Templates" côté Lore.
*/
export interface BottomPanel {
id: string; // identifiant pour mémoriser l'état ouvert/fermé
title: string;
items: BottomPanelItem[];
initiallyOpen?: boolean;
/** Action "+" inline dans le header — créer un item sans déplier le panneau. */
headerAction?: { label: string; route: string };
}
export interface SecondarySidebarConfig {
title: string;
/**
* Si défini, le titre devient cliquable et navigue vers cette route
* (ex: accueil de la campagne depuis n'importe quelle sous-page). Absent =
* titre statique (cas du Lore).
*/
titleRoute?: string;
items: TreeItem[];
createActions: SidebarAction[];
globalItems: GlobalItem[];
globalBackLabel: string;
globalBackRoute: string;
bottomPanel?: BottomPanel; // optionnel : présent côté Lore (Templates)
/** @deprecated Remplacé par bottomPanel. Gardé pour compat des callers campagne. */
footerLabel?: string;
}
/**
* Service de layout — contrôle l'affichage de la secondary sidebar
* et les données contextuelles de la sidebar globale.
*
* L'état d'expansion des items de l'arbre est maintenu au niveau du service
* (et non dans le composant secondary-sidebar), pour survivre aux
* destructions/recréations du composant lors des navigations (le *ngIf dans
* app.component.html détruit la sidebar à chaque `hide()`).
*/
@Injectable({ providedIn: 'root' })
export class LayoutService {
private config$ = new BehaviorSubject<SecondarySidebarConfig | null>(null);
private readonly expanded = new Set<string>();
readonly secondarySidebar$ = this.config$.asObservable();
show(config: SecondarySidebarConfig): void {
this.config$.next(config);
}
hide(): void {
this.config$.next(null);
}
isExpanded(id: string): boolean {
return this.expanded.has(id);
}
toggleExpanded(id: string): void {
if (this.expanded.has(id)) this.expanded.delete(id);
else this.expanded.add(id);
}
setExpanded(id: string, state: boolean): void {
if (state) this.expanded.add(id);
else this.expanded.delete(id);
}
}