import * as d3 from '../d3Modules'; import { Type_Structure, Type_DataSource, Type_IntervalDisplay, Type_DisaggregationGap, Type_Position } from '../types/Utils'; import { Type_PaperFormat, Type_PaperOrientation, Type_Shape } from '../Elements/ElementsAttributesConfig'; import { Class_NodeElement } from '../Elements/Node'; import { Class_StockShape } from '../Elements/StockShape'; import { Class_LinkElement } from '../Elements/Link'; import { Class_LegendConfig } from '../Elements/LegendGenerator'; import { Class_ProtoElement } from '../Elements/Element'; import { Class_ElementStyle } from '../Elements/Element'; import { NodePositioning } from '../Algorithms/NodePositioning'; import { Class_Sankey } from './Sankey'; import { Class_ZoneSelection } from '../Elements/SelectionZone'; import { Class_Tag } from './Tag'; import { Class_ContainerElement } from '../Elements/TextZone'; import { Class_ApplicationData } from './ApplicationData'; import { Class_ConnectionGestureHandler, Type_ConnectionDirection } from './ConnectionGestureHandler'; import { Class_NodeBase } from '../Elements/NodeBase'; /** * #680 — Mode de cadrage automatique persistant, piloté par les 3 boutons radio de la * barre d'ajustement (ComponetStretchButtons). Un seul mode actif à la fois, ou aucun : * - 'width' : toute la LARGEUR du diagramme reste visible bord à bord, en permanence. * - 'height' : toute la HAUTEUR du diagramme reste visible bord à bord, en permanence. * - 'full' : TOUT le diagramme reste visible et centré, en permanence. * - 'none' : aucun cadrage automatique (l'utilisateur a zoomé/désenclenché). * Un zoom manuel repasse le mode à 'none' (cf. Camera.zoom*). Persisté (SankeyPersistence). */ export type Type_AutoFitMode = 'none' | 'width' | 'height' | 'full'; /** * os#1352 — RÉGIME de référence du mode « échelle adaptée ». Deux règles, même mécanique * (échelle = échelle_réf × grandeur_courante / grandeur_réf), seule la GRANDEUR change : * * - `diagram` (défaut, sa#384) : grandeur = somme de la colonne la plus haute. Ne dépend * d'aucun élément en particulier, donc jamais mise en défaut par un élément absent. * - `element` : grandeur = taille rendue de l'ÉLÉMENT DE RÉFÉRENCE désigné (flux * `shape_is_reference_flux` ou nœud-stock `shape_is_reference_stock`). C'est la règle * d'avant sa#384, restituée pour la PORTABILITÉ des documents qui ont été composés * autour d'un élément de référence — cf. CARTOFOB, calé sur le stock « Bois sur pied », * dont la colonne dimensionnante change d'une vue d'essence à l'autre. */ export type Type_ScaleAdaptedReference = 'diagram' | 'element'; export type Type_EditionTool = 'node' | 'link'; export type Type_CreationTool = Type_EditionTool | 'text_zone' | 'line'; /** * OS#1315 — Ancrage du cadrage, orthogonal au mode de fit : * - 'center' : le mou disponible est réparti autour du contenu (fits centrés, * cadrage initial unique au chargement quand aucun fit n'est actif) ; * - 'top_left' : le monde (0,0) est épinglé au coin haut-gauche de l'écran — sans * fit actif, le chargement ne bouge JAMAIS la caméra (zoom 100 %) ; avec fit, * le contenu est calé en haut-gauche (marges) au lieu d'être centré. * Persisté (SankeyPersistence, défaut 'center'). */ export type Type_FitAnchor = 'center' | 'top_left'; export declare class Class_DrawingArea { application_data: Class_ApplicationData; nodePositioning: NodePositioning; d3_selection_zoom_area: d3.Selection | null; d3_selection: d3.Selection | null; d3_selection_bg_group: d3.Selection | null; d3_selection_bg: d3.Selection | null; d3_selection_grid: d3.Selection | null; d3_selection_elements_group: d3.Selection | null; d3_selection_elements_sankey_group: d3.Selection | null; d3_selection_handlers: d3.Selection | null; d3_selection_zone_select: d3.Selection | null; viewport_clip_id: string; private _viewport_chrome; private _scale_overrides; private _interactions; private _connection_gesture; static: boolean; is_unitary: boolean; /** * OS#1250 phase 2 — le fichier chargé est antérieur à 0.92 et ses coordonnées * doivent être normalisées UNE FOIS (ramenées près de l'origine). * * Remplace l'ancien drapeau public `to_recenter`, qui armait le garde de * recenter(). Ce protocole était BUGUÉ : `to_recenter` n'était jamais remis à * false par le bouton « recentrer », les 4 chemins de Toolbar, ni la migration * legacy — il restait donc collant. Or ViewsManager appelle recenter() sans * jamais l'armer : une fois le drapeau collé à true, chaque changement de vue * se mettait à décaler les positions des nœuds ET à réécrire les centres * persistés (#1231), silencieusement, selon ce que l'utilisateur avait fait * avant. La normalisation est désormais explicite, ponctuelle, et recenter() * ne touche plus au document. */ private _needs_legacy_normalization; markForLegacyNormalization(): void; /** id du nœud central d'un board unitaire (is_unitary). Posé par updateUnitaryStyles * à chaque (re)focalisation. areaAutoFit s'en sert pour caler ce nœud au CENTRE de la * fenêtre (au lieu de centrer la bbox), afin qu'il reste au même endroit d'un focus à * l'autre malgré l'asymétrie de l'étoile (entrées/sorties en nombre/largeur variables). */ unitary_center_node_id?: string; /** Mode d'affichage des valeurs de flux sur un board unitaire (is_unitary). * 'percent' = % de la somme entrée/sortie du nœud central (défaut historique), * 'value' = valeur brute, 'normalized' = ratio vs un flux de référence fixé à 1 * (sankey.normalised_link). Piloté par le sélecteur de l'en-tête du modal unitaire ; * lu par formatValueWithOption pour ne pas forcer le suffixe '%' hors mode percent. */ unitary_value_mode: 'percent' | 'value' | 'normalized'; /** Sélecteur CSS du conteneur DOM hôte où _initDraw append le SVG #draw_zoom. * Vaut '#sankey_app' pour le diagramme principal (zone de dessin de l'app). * Une DrawingArea détachée (ex: sankey unitaire rendu dans un modal) pointe * vers son propre conteneur, ce qui permet un rendu SIMULTANÉ de plusieurs * diagrammes sans collision de SVG. */ container_selector: string; /** Document HÔTE du conteneur de dessin. Null = document principal (cas normal). * Une DA rendue dans une fenêtre détachée (Document Picture-in-Picture / popup, * cf. PipWindow) pointe ici le `document` de cette fenêtre fille : la résolution * du conteneur (getContainerNode) cible alors le bon document, et tout le SVG est * construit dans la fenêtre fille (d3.append crée les nœuds dans le ownerDocument * du conteneur sélectionné). */ container_owner_document: Document | null; /** Élément DOM hôte de la zone de dessin, résolu dans le bon document * (`container_owner_document` si défini, sinon le document principal). Centralise * la résolution du conteneur pour qu'une DA détachée dans une autre fenêtre s'y * dessine. Peut renvoyer null si le conteneur n'est pas (encore) monté. */ protected getContainerNode(): HTMLElement | null; /** True quand la DA n'est pas la zone de dessin principale (rendue dans un * modal/panneau détaché). Sert à neutraliser les offsets liés aux menus * (navbar/footer) qui n'existent pas autour du conteneur détaché. */ get is_detached(): boolean; /** True quand l'utilisateur peut interagir (édition normale, ou publish + editable). * Une DA détachée (sankey unitaire en modal) est en lecture seule : pas d'édition, * et la grille ne se dessine pas (drawGrid teste grid_visible && editable). */ get editable(): boolean; drawing_link: boolean; bypass_redraws: boolean; bypass_compute_positions: boolean; private _in_full_draw; /** * OS#1246 — « sommes-nous dans un draw() complet de la zone de dessin ? ». * * Node.updateLinksPositions ne redessine un flux que si son ancrage a bougé * ≥1px ou si son DOM manque. Ce garde-fou ignore les changements de VALEUR — * il ne posait pas problème tant que Node.unDraw() cascadait link.unDraw() sur * tous les flux à chaque dessin de nœud : leur DOM disparaissait, donc ils * étaient tous redessinés et l'optimisation ne servait jamais sur un draw * complet. Depuis que les sont réutilisés (data-join keyé), elle est * devenue active et gelait la valeur et l'épaisseur des flux d'un data tag à * l'autre. * * On restaure donc la sémantique d'origine : un draw complet redessine tous * les flux ; l'optimisation ne s'applique plus qu'aux redraws partiels (drag), * son usage réel. */ isInFullDraw(): boolean; /** * Documentation (onglet Doc, SA#167) importée depuis un fichier source lors d'un * transfert de mise en page. La doc vit sur l'ApplicationData (partagée), or la DA * temporaire d'import partage le MÊME application_data que le diagramme courant : on * ne peut donc pas la transporter via application_data. On la stocke ici en transitoire, * lue par updateFrom (mode 'doc'). undefined = pas de doc importée (ex. source = vue). */ imported_documentation_markdown: string | undefined; imported_documentation_images: { [id: string]: string; } | undefined; /** Solver/reconciliation options loaded from an Excel "Options" sheet. Pre-fills the reconciliation dialog. */ mfa_options: Record; protected _height: number; protected _width: number; protected _zoom_width: number; protected _zoom_height: number; protected _k_horiz: number; protected _k_vert: number; protected _color: string; protected _grid_color: string; protected _grid_visible: boolean; protected _grid_size: number; protected _magnetic_nodes: boolean; protected _smart_guides: boolean; protected _connection_arrows_off: boolean; protected _paper_format: Type_PaperFormat; protected _paper_orientation: Type_PaperOrientation; protected _margin_top_mm: number; protected _margin_right_mm: number; protected _margin_bottom_mm: number; protected _margin_left_mm: number; protected _sankey: Class_Sankey; protected _legend: Class_LegendConfig; private _fit_margin; _scale: number; zoom_animations_enabled: boolean; protected _k_fit: number; get k_fit(): number; protected _import_export_above_below: boolean; get import_export_above_below(): boolean; set import_export_above_below(v: boolean); protected _disaggregation_gap_mode: Type_DisaggregationGap; get disaggregation_gap_mode(): Type_DisaggregationGap; set disaggregation_gap_mode(v: Type_DisaggregationGap); view_filter_kind: 'filter' | 'auto'; protected _disaggregation_gap_value: number | null; get disaggregation_gap_value(): number; set disaggregation_gap_value(v: number); protected _position_mode_suspended_selection: string | undefined; get is_position_mode_suspended(): boolean; /** #369 — Arme la suspension sur la sélection de datatags courante (appelé au chargement). */ suspendPositionModeUntilDataChange(): void; /** #369 — Lève la suspension (changement de datatag, ou choix explicite d'un mode). */ clearPositionModeSuspension(): void; /** * #369/os#1351 — Empreinte de la SÉLECTION AFFICHÉE : datatags ET viewtags. * * La suspension d'ouverture existe pour qu'un fichier ne saute pas à l'affichage ; elle doit * donc se lever au premier geste de NAVIGATION. Elle ne surveillait que les datatags — or un * diagramme dont les vues sont des view tags (CARTOFOB : une vue par essence) ne change jamais * de datatag en navigant : la suspension ne se levait jamais, et le mode restait « absolu » en * permanence, quel que soit le réglage (constaté le 14/08 sur la page publiée). * * Les deux dimensions comptent parce que les deux changent les VALEURS affichées, donc la * hauteur rendue — ce que les modes « échelle adaptée » et « proportionnel » ont justement * pour rôle de rattraper. */ private _selectedDataTagsFingerprint; protected _io_reorg_data_selection: string | undefined; /** * #378 — Amorce la mémoire de sélection SANS réorganiser (appelé au chargement d'un * fichier). Sans cet amorçage, le premier dessin comparerait la sélection du fichier * ouvert à celle du fichier précédent et réorganiserait tout : un diagramme se rouvrirait * avec un ordre de flux différent de celui qui a été enregistré. */ primeIOReorgOnDataSelection(): void; protected _has_been_laid_out: boolean; get has_been_laid_out(): boolean; /** * #378 — Relance la réorganisation auto de l'ordre des flux E/S si la sélection de * datatags a changé depuis le dernier dessin. Appelé par `drawElements` APRÈS le * placement des nœuds (l'ordre se déduit des positions et hauteurs effectivement * dessinées) et AVANT le dessin lui-même. * * `release_locks = false`, comme un déplacement de nœud : une bascule de sélection est une * NAVIGATION, pas le « recalcul automatique » explicite qui libère les cadenas du menu * « Ordre des flux E/S ». Un arrangement verrouillé par l'utilisateur survit donc au * changement de millésime, les flux libres se réordonnant autour de lui. Les nœuds réglés * sur `io_reorg_mode = 'none'` sortent d'eux-mêmes (cf. reorganizeIOLinks). * * Le premier appel (mémoire vide) ne fait qu'amorcer : on rouvre toujours sur l'ordre * enregistré. * * @returns true si une réorganisation a eu lieu. */ reorganizeIOOnDataSelectionChange(): boolean; /** * #369 — Mode d'affichage à APPLIQUER au dessin courant : celui du style global, sauf tant * que la suspension d'ouverture tient (alors : absolu). Lève la suspension au premier * changement de sélection de datatags — quel que soit le chemin emprunté (panneau de * filtres, topbar, frise de séquence, options de publication), puisqu'on compare l'état et * non l'événement. */ /** * #369/os#1351 — Mode de position RÉELLEMENT appliqué au dessin : celui du style global, ou * « absolu » tant que la suspension d'ouverture tient. Exposé en lecture pour que la * suspension soit observable (tests, diagnostic) sans passer par une frame de dessin. */ get effective_position_mode(): Type_Position; protected _effectivePositionMode(): Type_Position; gap_mode_override: Type_DisaggregationGap | undefined; get effective_gap_mode(): Type_DisaggregationGap; protected _font_size_locked: boolean; get font_size_locked(): boolean; set font_size_locked(v: boolean); /** * Multiplicateur appliqué à la font-size d'un label pour compenser le zoom. * Mode verrouillé : 1 / zoom_live → la taille écran (font_size px) reste * constante quel que soit le zoom. Mode déverrouillé : 1 → police native qui * scale avec le repère zoomé. Source unique pour tous les calculs de label. */ get font_compensation(): number; protected _size_locked: boolean; get size_locked(): boolean; set size_locked(v: boolean); protected _auto_fit_mode: Type_AutoFitMode; protected _fit_drag_dir: { x: number; y: number; } | null; get auto_fit_mode(): Type_AutoFitMode; set auto_fit_mode(v: Type_AutoFitMode); protected _last_fit_variant: Exclude; get last_fit_variant(): Exclude; protected _fit_anchor: Type_FitAnchor; get fit_anchor(): Type_FitAnchor; set fit_anchor(v: Type_FitAnchor); /** * #680 — Applique le cadrage correspondant au mode actif. Appelé : * - au clic sur un bouton (animated=true : geste utilisateur explicite, zoom cinématique), * - après un déplacement/ajout/suppression d'élément quand un mode est actif * (animated=false : re-cadrage silencieux « au fur et à mesure »). * force_when_locked=true : un mode explicite prime sur le verrou de taille (#1240). * Mode 'none' : ne fait rien (pas de cadrage automatique de la fenêtre). */ applyAutoFitMode(animated?: boolean): void; /** * OS#1315 — Cadrage « d'arrivée » (chargement / fin de chargement différée) : * - mode actif → le mode s'applique ; * - mode 'none' + ancrage 'center' → fit centré PONCTUEL (la caméra reste libre ensuite) ; * - mode 'none' + ancrage 'top_left' → origine (0,0) au coin du cadre, zoom 100 %. * Idempotent ; no-op en verrou de taille et sur le board unitaire. */ applyInitialFraming(): void; beginFitDrag(): void; accumulateFitDrag(dx: number, dy: number): void; endFitDrag(): void; /** * os#1372 — DATATAG DE RÉFÉRENCE du mode « échelle adaptée » : ids des tags qui désignent la * sélection sur laquelle le diagramme est calé. Persisté — c'est une propriété de la * COMPOSITION, au même titre que le régime de référence. * * Ce qu'il remplace : une grandeur CAPTURÉE au vol (`scale_adapted_ref_magnitude`), prise au * datatag qui se trouvait à l'écran quand le mode a pris effet. Personne ne l'avait choisie, * rien ne disait laquelle c'était, et la moindre correction de données la rendait fausse * puisqu'elle était figée dans le fichier. Ici la référence est ÉNONCÉE, et la grandeur s'en * déduit à chaque dessin (cf. `NodePositioningScaleAdapted.referenceDataTagMagnitude`). * * Une seule dimension peut être nommée : les autres gardent leur sélection courante. */ protected _scale_adapted_reference_datatag: string[]; get scale_adapted_reference_datatag(): string[]; set scale_adapted_reference_datatag(ids: string[]); protected _locked_fit_dirty: boolean; protected _locked_zoom_transform: d3.ZoomTransform | null; protected _locked_overflow_shrunk: boolean; protected _captureLockedReference(): void; /** * os#1371 — Le dézoom de secours du verrou de taille a-t-il un sens dans le mode courant ? * * NON sous « échelle adaptée » : les deux régulent la MÊME grandeur — la taille apparente du * diagramme — par deux moyens opposés. Le mode la tient côté DONNÉES (échelle valeur→pixel, * hauteur rendue constante) ; le verrou la tient côté CAMÉRA (dézoom dès qu'un datatag déborde * le cadrage de référence). Ensemble, ils s'annulent : les plafonds qui s'appliquent APRÈS le * mode (`maximum_node`, référence d'échelle par view tag) font déborder, le verrou dézoome, et * la taille que le mode venait de fixer change quand même. Deux asservissements sur la même * sortie. * * Le cadrage de référence, lui, continue d'être réappliqué à l'identique : c'est le * RÉTRÉCISSEMENT seul qui n'a pas de sens ici. Et cela ne vaut que pour un débordement dû au * CONTENU ; celui dû à la FENÊTRE (barre latérale ouverte, cf. `refreshWindowFraming`) reste * traité, il ne doit rien au mode. * * `size_locked` est posé dans des fichiers d'étude existants sans que leur auteur l'ait jamais * réglé : la garde vaut donc aussi, et surtout, pour l'existant. */ protected get locked_overflow_shrink_allowed(): boolean; protected _lockedContentOverflows(ref_k: number): boolean; protected createNewSankey(id?: string): Class_Sankey; protected createNewSelectionZone(): Class_ZoneSelection; _scaleValueToPx: d3.ScaleLinear; /** * os#1383 — ÉCHELLE EFFECTIVE de la frame : celle que le rendu et la légende utilisent, DÉRIVÉE * à chaque dessin de l'échelle de base `_scale` par la chaîne échelle adaptée → référence * d'épaisseur par view tag → plafond de hauteur de nœud. Transitoire, jamais persistée. * * Pourquoi un second champ : ces trois règles écrivaient jusqu'ici DANS `_scale`, l'échelle * absolue de l'utilisateur, en s'engageant à la restaurer à la frame suivante « sauf si une * autre source l'avait recalculée entre-temps ». Chacune voyait alors la surcharge de l'autre, * l'adoptait comme base, et l'échelle adaptée d'une vue finissait dans `user_scale` : le mode * absolu héritait de l'échelle du dernier datatag adapté, et le mode adapté repartait d'une * base déjà plafonnée. Mesuré sur CARTOFOB : le stock, à 1585 px partout à l'ouverture, * tombait à 619 px après un aller-retour absolu → adapté, puis 767 / 1585 / 773 selon la vue. * * Règle : `_scale` n'est écrit QUE par l'utilisateur (setter `scale`), le chargement et la * copie ; les règles de frame n'écrivent que `_scale_effective`, remis à `undefined` en tête * de chaque dessin (`beginScaleFrame`). Rien n'est à restaurer, donc rien ne peut dériver. */ protected _scale_effective: number | undefined; private _background_d3_groups_shift_x; private _background_d3_groups_shift_y; private _maximum_flux?; private _minimum_flux?; private _balance_marker_enabled; private _balance_marker_strategy; private _balance_marker_tolerance; private _minimap_open; private _scale_reference_by_viewtag; private _scale_adapted_reference; private _maximum_node?; private _minimum_node?; private _filter_link_value; private _filter_label; private _filter_unit; private _filter_link_value_px; private _filter_label_px; private _filter_node; private _filter_node_px; private _filter_stock; private _filter_stock_px; private _show_zero_links; private _show_orphan_nodes; private _type_data; private _data_source; private _interval_display; _selection_zone: Class_ZoneSelection; private _list_g_element_id; protected _group_to_select: string; private _mode; private _style_paint_source; private _place_container_shape; private _edition_tool; private _tool_sticky; private _ghost_link; protected _selection: { [id: string]: Class_ProtoElement; }; private _pointer_pos; private _node_contextualied; private _link_contextualied; private _contextualised_free_label; private _is_drawing_area_contextualised; /** * Zoom & positioning of drawing_area * if we want to move manually the drawing_area, we should use this variable * (see areaFitHorizontally && areaFitVertically) * @private * @memberof Class_DrawingArea */ private _zoomListener; /** * Creates an instance of Class_DrawingArea. * @param {ClassAbstract_ApplicationData} application_data * @memberof Class_DrawingArea */ constructor(application_data: Class_ApplicationData, id?: string); delete(): void; copyFrom(drawing_area_to_copy: Class_DrawingArea): void; _copyAttrFrom(drawing_area_to_copy: Class_DrawingArea): void; /** * Postprocessing drawing area after JSON affectation * @protected * @memberof Class_ApplicationData */ afterFromJSON(): void; /** * Exécute `fn` avec `bypass_redraws` posé, puis restaure l'état précédent * dans un `finally`. Corrige le footgun du flag global non-safe (#240) : une * exception entre le `= true` et le `= false` ne laisse plus la DA figée. * * L'état précédent est *restauré* (et non forcé à `false`), ce qui rend les * appels réentrants sûrs : un `withBypassRedraws` imbriqué dans un batch qui a * déjà posé le flag ne le relâche pas prématurément. * * @param redraw Si `true` (défaut) et qu'on est l'appel le plus externe * (le flag était `false` en entrée), déclenche un `draw()` final — c'est le * pattern classique `true → travail → false → draw()`. Passer `false` pour * les blocs qui ne redessinent pas (construction de tooltip, édition de * label, preview de tag) ou qui délèguent le rendu à l'appelant. */ withBypassRedraws(fn: () => T, redraw?: boolean): T; draw(): void; /** * Corps du draw complet. Extrait de draw() pour que `_in_full_draw` soit * refermé par un finally quoi qu'il arrive. */ private _drawBody; /** * OS#388 — Prise en compte d'un changement de la LARGEUR RÉSERVÉE du fenêtrage * (barre latérale ouverte/fermée/redimensionnée, colonne tableur/doc…). Le CONTENU * ne change pas : seul ce qui dépend de `window_fitting_width/height` doit être * rafraîchi. * * Passer par `draw()` était un contresens : il détruit et reconstruit l'intégralité * du SVG (unDraw + _initDraw + drawElements, nœuds, flux et libellés compris) pour * ne mettre à jour, en caméra libre, qu'un cadre et deux barres de défilement — et * sortait en prime le toast bloquant « Initialisation de la zone de dessin ». * * Ce qui est rejoué reprend, régime par régime, ce que faisait l'ancien couple * `areaAutoFit() + draw()` — SEULE la reconstruction du SVG disparaît : * - AUCUN cadrage automatique ('none') → **rien n'est recadré**, chrome seul — * format papier ET taille verrouillée compris : la caméra est à l'utilisateur, * un geste de fenêtrage ne la lui reprend pas ; * - taille VERROUILLÉE + cadrage automatique → cadrage figé réappliqué, avec le * rétrécissement de secours si le contenu ne rentre plus dans la zone réduite ; * - mode PAPIER + cadrage automatique → la page se recale dans le viewport ; * - mode de cadrage AUTOMATIQUE (largeur / hauteur / tout) ou board unitaire → * re-fit, compensation de police comprise. * * @memberof Class_DrawingArea */ refreshWindowFraming(): void; /** * OS#388 — Recadrage automatique itéré jusqu'à son POINT FIXE. * * `areaAutoFit` n'est pas une fonction pure de la place disponible : la réserve de * débordement des libellés est calculée sur le zoom COURANT (`k_live`, cf. * `skip_text_in_bbox` dans `_areaAutoFitCore`), et en police verrouillée les * libellés sont contre-scalés par 1/k. Le résultat dépend donc du cadrage d'où * l'on part — et l'écart n'est pas symétrique : une RÉDUCTION de la zone converge * du premier coup, un AGRANDISSEMENT ne reprend qu'une partie de la place rendue. * D'où l'effet de cliquet mesuré sur un diagramme réel en police verrouillée * (506 nœuds) : l'échelle perdait ~5 % au premier cycle ouverture/fermeture de la * barre latérale et ne revenait jamais à son point de départ. Défaut ANTÉRIEUR à * ce ticket — l'ancien `areaAutoFit() + draw()` cliquetait pareil, en pire. * * Quelques passes bornées suffisent : on s'arrête dès que l'échelle ne bouge plus. * Le coût reste sans commune mesure avec la reconstruction du SVG qu'on a supprimée. */ private _autoFitToFixedPoint; /** * OS#388 — Chrome de la zone de dessin : fond + cadre de viewport + découpe #g_clip * (drawBackground -> _updateViewportBorder), grille, puis barres de défilement et * translateExtent. Tout ce qui se cale sur `window_fitting_*`, donc sur la largeur * réservée — et rien d'autre : aucun élément Sankey n'est retouché. */ private _refreshWindowChrome; /** * Reinit d3 selections * @protected * @memberof Class_DrawingArea */ protected _initDraw(): void; /** * Draw grid for drawing area * @public * @memberof Class_DrawingArea */ drawGrid(): void; /** * OS#1246 — rejoue `drawElements()` sous sémantique de draw COMPLET * (`_in_full_draw = true`) SANS repasser par le cycle draw() (unDraw + * _initDraw + autoFit). Nécessaire quand un changement de VALEUR/épaisseur * (bascule de type de données affichées) impose de redessiner le contenu de * TOUS les flux, alors que leurs ancrages ne bougent pas : sans ce drapeau, * Node.updateLinksPositions garderait leur épaisseur périmée (cf. isInFullDraw). * @memberof Class_DrawingArea */ drawElementsAsFullDraw(): void; /** * Draw all elements inside drawing area * @memberof Class_DrawingArea */ /** * os#1372 — Flux à redessiner, accumulés pendant la phase de PLACEMENT d'un `drawElements`. * `null` hors de cette phase : `Node.updateLinksPositions` dessine alors immédiatement, comme * avant. Un `Set` parce qu'un même flux est touché par ses DEUX extrémités. */ private _deferred_link_draws; /** Vrai tant qu'on est dans la phase de placement (les dessins de flux sont différés). */ get defers_link_draws(): boolean; /** * os#1374 — Époque d'éventail. Un éventail, c'est les pointes de tous les flux d'un côté de * nœud, calculées ENSEMBLE par `Class_NodeElement.drawLinksArrow` : leur géométrie dépend de * la somme des épaisseurs du côté, donc aucune ne se calcule seule. * * Le cache de pointe d'un flux (`_arrow_shape`) était vidé au début de CHAQUE dessin de flux ; * `_drawArrow` le trouvait vide et redemandait au nœud l'éventail ENTIER — mesuré au chargement * de SOCLE Céréales : 1 823 éventails pour 1 823 flux, et 72 recalculs pour le pire nœud dans * une seule passe. Le coût est quadratique en nombre de flux par côté. * * Une pointe posée pendant l'époque courante n'est donc plus périmée : on la garde. L'époque * change à chaque `Class_Sankey.draw` — c'est-à-dire chaque fois que les positions ont pu * bouger. Hors de ce cadre (`keeps_arrow_caches` faux : glisser-déposer, réglage d'apparence, * `refreshArrow`), rien ne change : le cache est vidé comme avant. */ private _arrow_epoch; private _in_arrow_epoch; /** Époque courante ; un cache de pointe estampillé à cette valeur est à jour. */ get arrow_epoch(): number; /** Vrai pendant un `Class_Sankey.draw`, seul cadre où un cache de pointe survit. */ get keeps_arrow_caches(): boolean; beginArrowEpoch(): void; endArrowEpoch(): void; /** * os#1373 — Ouvre la phase « calculer les ancres » : à partir d'ici et jusqu'au flush, un nœud * qui repositionne ses flux ne fait que les inscrire. Idempotent : la phase court de l'entrée * de `drawElements` jusqu'à la fin du join des nœuds, et `Class_Sankey.draw` la rouvre pour * elle-même quand elle est appelée hors de ce cycle (post-traitement « flux droit »). */ openDeferredLinkDraws(): void; /** * Enregistre des flux à redessiner en fin de placement. Appelé par * `Node.updateLinksPositions`, qui garde la responsabilité de DÉCIDER lesquels ont bougé. */ deferLinkDraws(links: Class_LinkElement[]): void; /** * Dessine une fois chacun des flux accumulés, aux positions définitives. La file est fermée * AVANT le parcours : un dessin de flux peut déclencher celui d'un nœud, donc rentrer à * nouveau dans `updateLinksPositions` — qui doit alors dessiner tout de suite, pas ré-empiler. */ flushDeferredLinkDraws(): void; drawElements(): void; private _drawElementsBody; /** * #363 — Passe post-dessin : recale le bord gauche de chaque cadre englobant * (nœud ou ZDT `tied_to_nodes`) sur l'enveloppe LABELS INCLUS de ses membres, * puis redessine les seuls cadres qui ont bougé. Voir * `Class_NodeBase.refitTiedFrameToLabels` pour le pourquoi du grow-only et de * la restriction à l'horizontale. * * Ordre bottom-up : un cadre membre d'un autre cadre doit être recalé d'abord, * car `_computeEnvelopeBBox` lit sa géométrie LOGIQUE (`prefer_logical`) et non * son `getBBox()` — sans cet ordre, l'englobant se calerait sur un x périmé. * Même boucle de convergence que le ré-ancrage de `updateFrom`, avec le même * repli en cas de cycle d'attachement. */ private _refitTiedFramesToLabels; /** * OS#1246 — supprime du DOM les racine d'éléments (nœuds/flux/conteneurs) * qui ne correspondent plus à un élément affiché. Complément « exit » de la * réutilisation du sous-arbre #g_elements_sankey (cf. _initDraw). Un élément * effectivement dessiné a une d3_selection dont le nœud est connecté ; les * autres (invisibles, supprimés, sous seuil) ne sont pas dans l'ensemble * attendu et leur résiduel est retiré. */ private _sweepOrphanElementGroups; drawSelected(): void; getZoomScale(): number; closeAllMenus(): void; closeAllContextMenus(): boolean; eventsEnabled(): boolean; deleteNode(node: Class_NodeElement): void; deleteLink(link: Class_LinkElement): void; deleteContainer(c: Class_ContainerElement): void; addAllVisibleElementsToSelection(): void; addElementToSelection(element: Class_ProtoElement): void; selectOnly(element: Class_ProtoElement): void; /** * OS#1259 — Cible du clic « façon PowerPoint » sur un groupe de zones de texte, * par CLICS SUCCESSIFS (drill-down). `clicked` et ses cadres ZDT englobants * forment un chemin du plus englobant à la feuille cliquée : * [groupe externe, ..., cadre direct, feuille cliquée] * - rien de ce chemin n'est sélectionné -> on saisit le GROUPE le plus externe ; * - un élément du chemin est sélectionné -> on descend d'UN niveau vers la * feuille (clic suivant = on entre dans le groupe) ; * - la feuille est déjà sélectionnée -> on reboucle sur le groupe externe. * Scopé aux zones de texte (un cadre de nœuds n'est pas concerné). Renvoie null * si `clicked` n'est pas une ZDT membre d'un groupe de ZDT (sélection normale). * Partagé par le clic sur la forme (NodeEventsHandler) et sur le label * (DrawLabel, qui court-circuite l'autre chemin via stopPropagation). */ resolveContainerGroupClickTarget(clicked: Class_NodeBase): Class_NodeBase | null; removeElementFromSelection(element: Class_ProtoElement): void; deleteSelectedElements(): void; purgeSelectionOfElement(reset?: boolean): void; deleteSelectedLinks(): void; /** * Clean selection set of sankey elements * @memberof Class_DrawingArea */ purgeSelection(): void; deleteSelection(): void; copyNodes(node_ids: string[]): void; /** os#1340 (Ctrl+D) — duplique la sélection courante (nœuds + liens internes + zones). */ duplicateSelection(): void; /** os#1340 (alt-glisser = cloner) — duplique la sélection SANS décalage (copies sous les originaux). */ cloneSelectionInPlace(): void; updateScaleAtLinkValueSetting(previously_valued_count?: number): void; /** * @param center_on_content OS#1250 phase 2 — centre le contenu dans le viewport sur les * axes où il a du mou, au lieu de l'ancrer en haut à gauche avec les marges. Posé par * recenter() : c'est la définition même de « recentrer ». Les autres fits (changement de * data tag, redimensionnement…) gardent leur cadrage habituel. */ areaAutoFit(horiz?: boolean, force_when_locked?: boolean, center_on_content?: boolean, fill_axis_forced?: boolean, keep_zoom?: boolean): void; /** * #292 — Corps du cadrage automatique, extrait pour que areaAutoFit l'enveloppe dans un try/finally * (garde de réserve de gouttière : le fit vise la zone PLEINE) sans réindenter tout le corps. * Comportement strictement inchangé — ne pas appeler directement (passer par areaAutoFit). */ private _areaAutoFitCore; /** * Re-render all node/link labels so the zoom compensation applied to * font-size (see DrawLabelBase.getEffectiveFontSize) takes effect. En mode * verrouillé (#165), déclenché à la fois par areaAutoFit (changement de k_fit) * ET par le zoom molette (eventZoom, débouncé) : les labels ont été dessinés à * l'ancien multiplicateur, un fresh draw est requis pour mettre à jour la * font-size et les offsets de positionnement dépendants. */ /** * Force le recalcul de la font-size des labels (compensation 1/k) sur le zoom * COURANT, sans condition. À utiliser avant un export : `_pre_process_export_svg` cale le * zoom sur le fit (areaAutoFit) mais areaAutoFit ne rafraîchit les labels que si k_fit a * changé — or ils peuvent porter la compensation d'un zoom manuel (ou d'une frame précédente), * d'où une police non réajustée à l'échelle d'export. Cet appel garantit la cohérence * font_size/k au moment de la capture. */ refreshLabelsForExport(): void; private _refreshLabelsForFitZoom; /** * Transpose the diagram (self-inverse): swap x↔y for all nodes/containers, * swap link orientations, swap DA dimensions, swap capsule↔capsule_h shapes, * and swap label horiz↔vert positions. Calling twice restores original state. */ verticalizeDiagram: () => void; inverseSelectedLinks: () => void; /** * Update tag selected for selected links and save it undoing * * @param {boolean} val * @param {Class_Tag} flux_tag */ updateSelectedLinksTagAssignation: (val: boolean, flux_tag: Class_Tag) => void; /** * Update tag selected for selected nodes and save it undoing * * @param {boolean} val * @param {Class_Tag} flux_tag */ updateSelectedNodesTagAssignation: (val: boolean, node_tag: Class_Tag) => void; /** * Function to delete attr _minimum_flux * * @memberof Class_DrawingArea */ removeMinimumLinkThickness(): void; /** * Function to delete attr _maximum_flux * * @memberof Class_DrawingArea */ removeMaximumLinkThickness(): void; /** * Function to delete attr _minimum_node * * @memberof Class_DrawingArea */ removeMinimumNodeHeight(): void; /** * Function to delete attr _maximum_node * * @memberof Class_DrawingArea */ removeMaximumNodeHeight(): void; /** * Create a timed out process - Used to avoid multiple reloading of components * * The process_func is meant to be use by setTimeout(), * and inside setTimeOut 'this' keyword has another meaning, * so the current object must be passed directly as an argument. * see : https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#the_this_problem * * @protected * @param {string} process_id * @param {(_: Class_ProtoElement) => void} process_func * @memberof ClassTemplate_ProtoElement */ protected _process_or_bypass(process_func: () => void): void; /** * Swaps overlaps position of element on DA * * @param {number} idx_src * @param {number} idx_trgt * @memberof Class_DrawingArea */ moveOrderElementInDA: (idx_src: number, idx_trgt: number) => void; /** * #1259 — Envoie un cadre de groupe DERRIÈRE ses membres dans l'ordre Z. * Convention réelle (orderElementOnDA trie sur la liste INVERSÉE) : un élément * plus loin dans `_list_g_element_id` est dessiné plus tôt = plus au FOND. * Donc « derrière » = indice PLUS HAUT. On place le cadre juste après (indice * plus haut que) le membre le plus au fond. Sans ça, le cadre capte les clics * de ses membres (cas ZDT dans ZDT : impossible d'attraper la ZDT membre). */ sendFrameBehindMembers(frame: Class_NodeBase): void; /** * OS#1259 — Normalise l'ordre Z des cadres de la LÉGENDE après (re)génération. * * Le générateur (regenerateLegend) crée le cadre racine 'legend' AVANT ses * zones : le constructeur de Class_ContainerElement poussant chaque id à la * fin de `_list_g_element_id`, le cadre se retrouve à un indice PLUS BAS que * ses membres, c'est-à-dire DEVANT eux — il capte alors les clics destinés aux * entrées de légende, exactement ce que sendFrameBehindMembers corrige pour * les groupes. * * Cette normalisation n'était appliquée qu'au CHARGEMENT (DrawingAreaPersistence. * fromJSON), et seulement si les conteneurs venaient du fichier. D'où une * non-idempotence du round-trip sur les fichiers legacy sans conteneurs de * légende (0.91) : 1er chargement = légende générée après le chargement, cadre * devant ses membres ; 2e chargement = conteneurs relus depuis le JSON, donc * normalisés, cadre derrière. On applique donc l'invariant à la source, à * chaque régénération. * * Les blocs de groupe ('legend-block-*', plus profonds) sont traités avant le * cadre racine pour que celui-ci finisse derrière toute sa descendance. */ private _sendLegendFramesBehindMembers; /** * #242 — Retire un élément de la liste des tracés (utilisé quand le lien fantôme est * détruit à la fin d'un geste de création). */ forgetGElementId(id: string): void; orderElementOnDA(): void; moveOrderStyleInSelectedElements: (style_src: Class_ElementStyle, style_trgt: Class_ElementStyle) => void; unDraw(): void; /** * OS#1250 phase 2 — NORMALISATION des coordonnées monde. Migration PONCTUELLE * des fichiers antérieurs à 0.92, dont le cadrage d'origine reposait sur le * recentrage mutant fait au chargement (invariant #1231 : ils doivent * toujours s'ouvrir cadrés). * * C'est le SEUL endroit qui déplace encore le monde. Le code ci-dessous est * celui de l'ancien recenter(), extrait tel quel : il ramène le contenu près * de l'origine. Il tourne après le premier draw (cf. ApplicationData.fromJSON), * donc la bbox DOM est disponible — la mesure depuis le MODÈLE viendra en * phase 3. */ normalizeLegacyWorldCoordinates(): void; /** * OS#1250 phase 2 — recenter est désormais une opération de CAMÉRA : il cadre * le contenu, il ne le DÉPLACE plus. * * Avant, il décalait `position_x/y` de tous les nœuds, les centres persistés * (#1231), les positions de labels, les containers et la légende — donc un * clic sur « recentrer » modifiait le document, et une opération de navigation * touchait la persistance. Ce décalage ne survit que comme migration ponctuelle * des fichiers < 0.92 (normalizeLegacyWorldCoordinates). * * L'ancien garde `if (!this.to_recenter) return` a disparu avec le protocole : * cf. `_needs_legacy_normalization` pour le bug de drapeau collant qu'il * causait. Le calcul de canvas (width/height) a lui aussi disparu : areaAutoFit * le refait avec la même formule juste en dessous. */ /** * os#1383 — Marque le cadrage VERROUILLÉ à refaire : le prochain `draw()` recalcule le cadrage * de référence comme à l'ouverture (fit sur la géométrie fraîchement dessinée, puis capture), * au lieu de reposer la caméra figée. C'est le geste d'un changement de VUE — une sélection * d'étiquette de vue recompose le diagramme, ce n'est pas un défilement de datatag — et c'est * ce que fait l'application quand on passe d'une essence à l'autre. Sans lui, un viewer * embarqué gardait la caméra resserrée d'une essence peu fournie en revenant à la vue agrégée. * No-op hors verrou de taille : les autres modes se recadrent déjà d'eux-mêmes. */ invalidateLockedFit(): void; recenter(force?: boolean): void; /** Variante animée des recadrages EXPLICITES (boutons fit H/V). */ areaAutoFitAnimated(horiz?: boolean, force_when_locked?: boolean, fill_axis_forced?: boolean): void; /** Variante animée du bouton « recentrer ». */ recenterAnimated(force?: boolean): void; /** Centre la caméra sur un nœud, avec animation (recherche / sélection). */ flyToNode(node: Class_NodeElement, scale?: number): void; /** Zoom explicite par facteur multiplicatif (boutons -/+), ancré au centre du viewport. */ zoomByFactor(factor: number): void; /** Zoom explicite vers une échelle absolue (clic indicateur → 100% = k=1). */ zoomToScale(k: number): void; /** Fige le zoom courant dans la géométrie (tailles ×ratio) et remet la caméra à 100 %. */ bakeZoomIntoGeometry(opts?: { record_history?: boolean; }): void; /** Centre la caméra sur un point MONDE, avec animation (recherche flux / zone — OS#1273). */ flyToPoint(wx: number, wy: number, scale?: number): void; /** Viewport utile en pixels écran (réserves de panneaux déduites) + décalage de la nav bar. */ getViewport(): { width: number; height: number; top_offset: number; }; /** Bounds du contenu en coordonnées MONDE (null si vide). */ contentBounds(): { x: number; y: number; width: number; height: number; } | null; /** * Fonction PURE : transform de caméra qui cadre `bounds` dans `viewport` avec la marge donnée * (contenu ancré en haut-gauche à margin/2, comme le fit historique). */ fitTransform(bounds: { x: number; y: number; width: number; height: number; }, viewport: { width: number; height: number; top_offset: number; }, margin?: number): d3.ZoomTransform; /** Transform caméra courant (null tant que le SVG de zoom n'existe pas). */ getCameraTransform(): d3.ZoomTransform | null; /** * OS#1315 — Cale le monde (0,0) au coin haut-gauche du CADRE visible, zoom 100 %. * Même référence que les fits ((fit_margin/2, navbar + fit_margin/2)) — PAS le pixel * (0,0) absolu de l'écran, qui serait masqué sous la barre de navigation. */ resetCameraToOrigin(): void; /** Point d'application UNIQUE d'un transform de caméra. */ setCamera(target: d3.ZoomTransform, opts?: { animate?: boolean; from?: d3.ZoomTransform; }): void; /** * Recadrage de fit : échelle `k` puis point MONDE (0,0) au pixel [px, py]. Conserve le constrain * de d3-zoom (load-bearing pour le ré-ancrage des labels en police verrouillée, #165) ; * _updateScrollbars doit avoir été appelé AVANT (il pose le translateExtent lu par le constrain). */ /** OS#1250 phase 1 — ré-ancrage sans changement d'échelle (cf. Camera.anchorCamera). */ private _anchorCamera; private _applyFitCamera; /** * Draw background for drawing area * * @param {*} drawing_area */ /** * Bornes (coords monde, dans g_drawing) du fond et de la grille en mode libre. * = UNION du canvas (zoom area) et du VIEWPORT VISIBLE converti en coords monde. * Le viewport (= viewport_border : [fm, navH+fm, viewW, viewH] en écran) garantit * que le fond/grille remplissent toujours toute la fenêtre, quel que soit le pan/zoom * ou le ré-ancrage (#165) — le canvas figé, lui, ne couvre plus la fenêtre après un * ré-ancrage ou quand le contenu est plus petit qu'elle. */ /** * OS#1250 phase 4 — CANVAS INFINI : bornes du fond et de la grille en mode libre. * * C'est exactement le viewport projeté en coordonnées monde : le fond couvre ce qu'on * voit, ni plus ni moins. Il n'y a plus de « canvas » — ce rectangle fini, dimensionné * sur la fenêtre puis unionné ici, n'existait que pour être ancré par le constrain * custom (supprimé en phase 5). Le fond suivait donc une géométrie dont personne * n'avait plus besoin, et grossissait au fil des pans. * * Le résultat dépend de la caméra (et non de l'inverse) : c'est le sens du modèle. * Marge de sécurité d'un demi-viewport de chaque côté pour absorber les pans/zooms * entre deux redraws (le zoom n'applique qu'un transform ; le fond n'est redessiné * qu'en différé, cf. eventZoom) sans laisser apparaître de bande vide. */ private _freeBgBounds; protected drawBackground(): void; /** * Position and size the viewport border rect on the SVG root (outside g_drawing). * Mode libre : encadre la zone visible (fenêtre). Mode papier : encadre la PAGE. * Appelé à l'init, à chaque drawBackground(), et — en mode papier — à chaque zoom/pan * (eventZoom), car le fond n'y est pas redessiné. */ private _updateViewportBorder; /** * History saving * @param f */ saveUndo(f: (_: Class_DrawingArea) => void): void; /** * History saving * @param f */ saveRedo(f: (_: Class_DrawingArea) => void): void; /** * Test if mouse is over some node * * @private * @return {*} * @memberof Class_DrawingArea */ isMouseOverAnExistingNode(): boolean; /** * Set up events related to element d3_element * @private * @memberof Class_DrawingArea */ private setEventsListeners; /** * Rafraîchit les scrollbars (et, au passage, extent/translateExtent du zoom listener — * cf. Class_ViewportChrome.updateScrollbars, qui calcule l'étendue pannable). */ private _updateScrollbars; /** * OS#1250 — recale le translateExtent et les scrollbars sur la bbox de contenu * courante SANS toucher à la caméra. À utiliser quand le contenu s'étend (drag * d'un nœud au-delà de l'ancienne bbox) : le monde grandit, la vue reste où * l'utilisateur l'a mise. */ refreshPanExtent(): void; /** * Define behavior when we scroll in drawing area (or scroll zone around) * && when we drag mouse middle button in drawing area (or scroll zone around) * * @private * @param {*} e * @memberof Class_DrawingArea */ private eventZoom; isInSelectionMode(): boolean; setSelectionMode(): void; isInEditionMode(): boolean; protected setEditionMode(): void; get edition_tool(): Type_EditionTool; isInNodeTool(): boolean; isInLinkTool(): boolean; get tool_sticky(): boolean; set tool_sticky(v: boolean); /** Outil de création actif, ou null si l'on est en sélection / application de style. */ get active_creation_tool(): Type_CreationTool | null; /** * Active un outil de création (ou le désactive si c'est déjà lui = bascule). Point * d'entrée UNIQUE des quatre boutons de la colonne d'outils : il aiguille vers le * bon mode moteur et remet à zéro l'état de l'outil précédent. */ setCreationTool(tool: Type_CreationTool | null, sticky?: boolean): void; /** * Fin d'une pose : on rend la main à la sélection, sauf si l'outil est verrouillé * (double-clic sur son bouton). Appelé par les gestionnaires de geste APRÈS que * l'élément créé a été sélectionné — l'auteur enchaîne donc naturellement sur son * réglage dans l'inspecteur. */ finishToolGesture(): void; /** * Re-pose les écouteurs des éléments après un changement de mode : le drag des nœuds * et des zones est branché/débranché selon le mode (cf. switchMode). */ refreshModeEventsListeners(): void; /** Rafraîchit la colonne d'outils (état actif/inactif des boutons). */ notifyToolsColumn(): void; isInStylePaintMode(): boolean; get style_paint_source(): Class_ProtoElement | null; enterStylePaintMode(source: Class_ProtoElement): void; exitStylePaintMode(): void; isInPlaceContainerMode(): boolean; get place_container_shape(): Type_Shape; enterPlaceContainerMode(shape?: Type_Shape): void; exitPlaceContainerMode(): void; applyStyleFromPaintSource(target: Class_ProtoElement): void; /** * Propage le style du nœud `source` à toute sa descendance dans la hiérarchie * de dimensions (désagrégation), même si les enfants sont actuellement * agrégés/masqués, en une seule transition d'historique. */ applyStyleToNodeChildren(source: Class_NodeElement): void; /** Variante multi-sélection de {@link applyStyleToNodeChildren}. */ applyStyleToNodesChildren(sources: Class_NodeElement[]): void; /** * Assigne la colonne du nœud parent à toute sa descendance (cf. styleCascade), en une seule * transition d'historique. */ assignColumnToNodesChildren(sources: Class_NodeElement[]): void; /** * Propage le style du flux `source` à ses flux enfants (ceux reliant un descendant de sa source * à un descendant de sa cible), en une seule transition d'historique. */ applyStyleToLinkChildren(source: Class_LinkElement): void; /** Variante multi-sélection de {@link applyStyleToLinkChildren}. */ applyStyleToLinksChildren(sources: Class_LinkElement[]): void; switchMode(): void; setToModeEdition(_: boolean): void; /** * Technically don't draw a cursor but add a class & then css use it to modify cursor * * @memberof Class_DrawingArea */ drawCursor(): void; get sankey(): Class_Sankey; get legend(): Class_LegendConfig; set legend(value: Class_LegendConfig); get ghost_link(): Class_LinkElement | null; set ghost_link(value: Class_LinkElement | null); get selected_elements_list(): Class_ProtoElement[]; get selected_visible_elements_list(): Class_ProtoElement[]; get selected_nodes_list(): Class_NodeElement[]; get selected_links_list(): Class_LinkElement[]; get selected_containers_list(): Class_ContainerElement[]; get selected_stock_shapes_list(): Class_StockShape[]; get selected_nodes_list_sorted(): Class_NodeElement[]; get selected_links_list_sorted(): Class_LinkElement[]; get selected_containers_list_sorted(): Class_ContainerElement[]; get width(): number; set width(_: number); get height(): number; set height(_: number); get window_fitting_height(): number; get main_zone_bottom_reserved(): number; /** Réserve des panneaux qui S'OUVRENT et se FERMENT — barre latérale (config, * filtres, modèles) et colonne tableur/doc/unitaire. EXCLUT la colonne d'outils, * permanente : elle est là au repos, la rendre ferait dessiner sous les boutons. * Nulle sur une zone détachée, dont le cadrage suit son conteneur hôte. */ get panel_reserve_right(): number; /** Symétrique en bas : réserve de la doc en mode bandeau. */ get panel_reserve_bottom(): number; /** Largeur du CHROME. Identique à `window_fitting_width` dès qu'un cadrage * automatique est actif : le chrome épouse alors la zone cadrée, gouttière de barre * de défilement comprise (#292 — la barre se loge HORS du dessin). * * En caméra libre au contraire, RIEN ne rabote la zone : ni la réserve du panneau, * ni la gouttière. La barre se dessine PAR-DESSUS le bord du diagramme — la loger * hors du dessin obligerait à rétrécir la zone, c'est-à-dire exactement le * recadrage qu'on doit s'interdire ici. */ get chrome_fitting_width(): number; /** Hauteur du CHROME (cf. `chrome_fitting_width`). */ get chrome_fitting_height(): number; get main_zone_right_reserved(): number; private _scrollbar_reserve_right; private _scrollbar_reserve_bottom; get scrollbar_reserve_right(): number; set scrollbar_reserve_right(v: number); get scrollbar_reserve_bottom(): number; set scrollbar_reserve_bottom(v: number); private _suppress_scrollbar_reserve; get suppress_scrollbar_reserve(): boolean; get window_fitting_width(): number; get is_paper_mode(): boolean; get paper_format(): Type_PaperFormat; set paper_format(fmt: Type_PaperFormat); get paper_orientation(): Type_PaperOrientation; set paper_orientation(o: Type_PaperOrientation); get margin_top_mm(): number; set margin_top_mm(v: number); get margin_right_mm(): number; set margin_right_mm(v: number); get margin_bottom_mm(): number; set margin_bottom_mm(v: number); get margin_left_mm(): number; set margin_left_mm(v: number); /** Convert mm to CSS px (96 DPI standard) */ static mmToPx(mm: number): number; /** Convert CSS px to mm */ static pxToMm(px: number): number; /** Get paper dimensions in mm respecting orientation */ getPaperDimensionsMm(): { width: number; height: number; }; /** * Dimensionne le canvas papier : le plus petit rectangle AU RATIO DU FORMAT qui contient * le diagramme, centré dessus. La dimension contraignante — largeur ou hauteur — est * choisie d'après le contenu (autofit à ratio constant). * * Seul le RATIO compte ici, pas les millimètres : A3/A4/A5 partagent 1:√2 et ne se * distinguent qu'à l'export (qui relit getPaperDimensionsMm(), inchangé). Seule * l'orientation change le rapport (1.414 ↔ 0.707). * * La CAMÉRA n'est pas touchée : changer de format/orientation ne bouge ni le diagramme * ni le zoom. Seuls le fond, la grille et le cadre (tous dimensionnés sur _zoom_* et les * shifts) s'adaptent. Sans contenu, on retombe sur les dimensions physiques du format. */ protected applyPaperDimensions(): void; /** * Return height of the top nav bar * * @return {*} * @memberof Class_DrawingArea */ getNavBarHeight(): number; /** * Return height of the top nav bar * * @return {*} * @memberof Class_DrawingArea */ getBottomBarHeight(): number; get color(): string; set color(_: string); /** Échelle EFFECTIVE de la frame (cf. `_scale_effective`) — celle du rendu et de la légende. */ get scale(): number; /** Échelle de BASE, absolue, celle de l'utilisateur et du fichier (`user_scale`). */ get base_scale(): number; set scale(value: number); get grid_color(): string; set grid_color(_: string); get grid_visible(): boolean; set grid_visible(_: boolean); setGridVisible(): void; setGridInvisible(): void; get grid_size(): number; set grid_size(_: number); get connection_gesture(): Class_ConnectionGestureHandler; /** * os#1344/os#1347 — CONTRAINTE DE POSITION pour la création connectée : où poser un * nœud créé depuis un nœud source dans une direction donnée. Espacement raisonnable * (fonction du pas de grille), aligné sur la grille quand le magnétisme est actif * (même pas que moveMagneticNode : grid_size / 4). Le handler de geste ne calcule * rien lui-même : c'est la zone de dessin qui fournit la règle. */ getConnectedCreationPosition(source: Class_NodeBase, direction: Type_ConnectionDirection, width: number, height: number): { x: number; y: number; }; get selection_zone(): Class_ZoneSelection; get node_contextualised(): Class_NodeElement | undefined; set node_contextualised(value: Class_NodeElement | undefined); get link_contextualised(): Class_LinkElement | undefined; set link_contextualised(value: Class_LinkElement | undefined); get contextualised_container(): Class_ContainerElement | undefined; set contextualised_container(value: Class_ContainerElement | undefined); get pointer_pos(): [number, number]; set pointer_pos(value: [number, number]); get is_drawing_area_contextualised(): boolean; set is_drawing_area_contextualised(value: boolean); get maximum_flux(): number | undefined; set maximum_flux(value: number | undefined); get scale_reference_by_viewtag(): { [view_tag_id: string]: { link_id: string; thickness: number; }; }; /** * os#1352 — Régime de référence du mode « échelle adaptée » (cf. Type_ScaleAdaptedReference). * Changer de régime invalide la capture en cours : les deux grandeurs ne sont pas comparables * (hauteur de colonne vs taille d'un élément), recomposer un ratio de l'une sur l'autre ferait * sauter l'échelle. La capture paresseuse repart au dessin suivant, ratio 1, sans saut. */ get scale_adapted_reference(): Type_ScaleAdaptedReference; set scale_adapted_reference(v: Type_ScaleAdaptedReference); /** * Désigne (ou retire) le flux de référence d'échelle pour un view tag donné. * `link_id` falsy ou `thickness <= 0` → retire la référence du view tag. * Sinon écrase l'éventuelle référence existante (un seul flux par view tag). */ setScaleReferenceForViewTag(view_tag_id: string, link_id: string | undefined, thickness: number): void; /** * Référence d'épaisseur du view tag courant : recalcule l'échelle pour que le flux de référence * atteigne son épaisseur cible (cf. Class_ScaleOverrides — surcharge transitoire, restaurée à la * frame suivante). */ applyViewTagScaleReference(): void; /** * #1231b — Plafond de hauteur de nœud appliqué par l'ÉCHELLE (cf. Class_ScaleOverrides). */ applyMaximumNodeScale(exact?: boolean): void; /** * os#1383 — Tête de frame : l'échelle effective repart de la base. Appelé par `drawElements` * avant les trois règles d'échelle (adaptée, référence de vue, plafond). */ beginScaleFrame(): void; /** * os#1383 — Pose l'échelle EFFECTIVE de la frame (cf. `_scale_effective`) : le domaine * valeur→px suit, `_scale` — l'échelle absolue de l'utilisateur — ne bouge pas. */ setEffectiveScale(value: number): void; get balance_marker_enabled(): boolean; set balance_marker_enabled(value: boolean); get balance_marker_strategy(): 'exact' | 'absolute' | 'relative'; set balance_marker_strategy(value: 'exact' | 'absolute' | 'relative'); get balance_marker_tolerance(): number; set balance_marker_tolerance(value: number); get minimap_open(): boolean; set minimap_open(value: boolean); get minimum_flux(): number | undefined; set minimum_flux(value: number | undefined); get maximum_node(): number | undefined; set maximum_node(value: number | undefined); get minimum_node(): number | undefined; set minimum_node(value: number | undefined); get scaleValueToPx(): d3.ScaleLinear; get filter_label(): number; set filter_label(value: number); get type_data(): Type_Structure; set type_data(value: Type_Structure); get data_source(): Type_DataSource; set data_source(value: Type_DataSource); get interval_display(): Type_IntervalDisplay; set interval_display(value: Type_IntervalDisplay); /** * True when the diagram is in a "structure-like" display: * - data_source === 'structure' (whole diagram is structure-only), OR * - data_source === 'reconciled' AND interval_display === 'structure' AND * the sankey actually has intervals (= the interval-display selector was * visible and the user explicitly picked "structure" in it). * * The `has_intervals` guard is critical : `interval_display === 'structure'` * is *also* the legacy default for reconciled diagrams without intervals, * where the user just wants normal proportional thicknesses. Without the * guard, every legacy file would load in forced-min mode. */ get is_structure_display(): boolean; get filter_link_value(): number; set filter_link_value(value: number); get filter_unit(): 'value' | 'pixel'; set filter_unit(value: 'value' | 'pixel'); get filter_link_value_px(): number; set filter_link_value_px(value: number); get filter_label_px(): number; set filter_label_px(value: number); get filter_node(): number; set filter_node(value: number); get filter_node_px(): number; set filter_node_px(value: number); get filter_stock(): number; set filter_stock(value: number); get filter_stock_px(): number; set filter_stock_px(value: number); /** * Seuil d'affichage du LABEL d'un nœud selon l'unité active (#seuil px). * @param value valeur de donnée du nœud (data_value) * @param height_px hauteur de bande rendue du nœud (getShapeHeightToUse) * @returns true si le nœud passe le seuil (label affiché). */ nodeLabelPassesThreshold(value: number, height_px: number): boolean; /** * Seuil d'affichage du LABEL de stock selon l'unité active (#seuil px). * * IMPORTANT : la hauteur rendue d'un stock N'EST PAS scaleValueToPx(|valeur|) — * elle est divisée par le facteur d'échelle stock par nœud (stock_height_scale_factor, * cf. Node.tsx _getNaturalShapeHeight / drawStockBox). On exige donc que l'appelant * fournisse la hauteur RÉELLEMENT rendue (repère local), pour que le seuil px « stock » * soit sur la même échelle visuelle que le seuil px « flux ». * @param abs_value magnitude du stock (|stock initial|), ou null (jamais masqué) * @param height_px hauteur de bande RENDUE du stock (repère local, facteur inclus) */ stockLabelPassesThreshold(abs_value: number | null, height_px: number): boolean; /** * true si un seuil d'affichage exprimé en pixels est actif (unité pixel + au moins * un seuil > 0). Sert à ne redéclencher un re-tracé au zoom que quand c'est utile * (les pixels écran dépendent du zoom, cf. handlers de zoom). */ get has_active_pixel_filter(): boolean; get show_zero_links(): boolean; set show_zero_links(value: boolean); get show_orphan_nodes(): boolean; set show_orphan_nodes(value: boolean); get fit_margin(): number; /** * #242 — Comportement de zoom/pan d3, exposé pour le chrome de viewport : le drag d'un pouce de * scrollbar déplace la caméra, et `updateScrollbars` (re)pose extent/translateExtent. */ get zoomListener(): d3.ZoomBehavior; /** * #242 — Rectangle du canvas de fond (coords monde) : la PAGE en mode papier, le fond décalé en * mode libre. Sert au cadre de viewport et au tracé du fond. */ get background_canvas_rect(): { x: number; y: number; w: number; h: number; }; /** * #242 — Rectangle « canvas » (coords monde) servant de plancher à l'étendue pannable : uni au * contenu, il donne le translateExtent de d3-zoom et l'échelle des scrollbars. En mode papier * c'est la page ancrée en (0,0) — et non le fond décalé. */ /** * OS#1250 phase 4 — la PAGE, rect fixe en coordonnées monde. Le mode papier n'est * plus un « canvas » à ancrer : c'est un rectangle comme un autre, qui participe aux * bounds (cf. Class_ViewportChrome.updateScrollbars) et sert de bornes au fond/grille. * Origine (0,0) par construction : les positions du mode papier sont calculées pour * le format. */ get paper_world_rect(): { x: number; y: number; w: number; h: number; }; get pannable_canvas_rect(): { x0: number; y0: number; x1: number; y1: number; }; get magnetic_nodes(): boolean; set magnetic_nodes(value: boolean); get smart_guides(): boolean; set smart_guides(value: boolean); get connection_arrows_off(): boolean; set connection_arrows_off(value: boolean); get list_g_element(): string[]; set list_g_element(list: string[]); d3_selection_def_gradient: d3.Selection | null; private _show_background_image; private _background_image; private _constrain_to_bg_image_ratio; private _bg_image_natural_ratio; private _bg_image_horizontal_align; drawBgImage(): void; /** True when the drawing area is constrained to the background image's aspect ratio. */ get is_bg_image_ratio_mode(): boolean; /** Adjust height so width/height matches the background image's natural ratio. */ applyBgImageRatio(): void; /** Load natural dimensions of the bg image (async) and re-apply ratio constraint. */ private _loadBgImageNaturalRatio; bgGrid: () => void; maskLegend: () => void; changeScale: (evt: number | null | undefined) => void; setParametricMode(): void; setAbsoluteMode(): void; setScaleAdaptedMode(redraw?: boolean): void; setProportionalMode(): void; resetAllVerticalIntervals(v_spacing?: number): void; get id(): string; get name(): string; set name(name: string); get show_background_image(): boolean; set show_background_image(value: boolean); get background_image(): string; set background_image(value: string); get constrain_to_bg_image_ratio(): boolean; set constrain_to_bg_image_ratio(value: boolean); get bg_image_horizontal_align(): 'left' | 'center' | 'right'; set bg_image_horizontal_align(value: 'left' | 'center' | 'right'); }