import * as d3 from '../d3Modules'; import { Type_LangMap } from '../Persistence/persistenceMigrations'; import { Class_ElementStyle } from './Element'; import { NodeDrawNameLabel } from './DrawLabel'; import { Class_DrawingArea } from '../types/DrawingArea'; import { NodeDrawShape } from './NodeDrawShape'; import { Class_BaseShape } from './Element'; import { NodeEventsHandler } from './NodeEventsHandler'; export declare const default_selected_stroke_width = 3; export type Type_NameLabelSource = 'name' | 'custom' | 'tag' | 'ancestor' | 'template'; export declare function sortNodesElements(a: Class_NodeBase | Class_ElementStyle, b: Class_NodeBase | Class_ElementStyle): 1 | 0 | -1; export declare abstract class Class_NodeBase extends Class_BaseShape { private _drag_handler; private _line_endpoint_handler; _nodeEventsHandler: NodeEventsHandler; d3_selection_g_shape: d3.Selection | null; private _position_u; private _position_v; protected _name_map: Type_LangMap; protected _nodeDrawShape: NodeDrawShape; protected _nodeDrawNameLabel: NodeDrawNameLabel; protected _nodeDrawIcon: NodeDrawNameLabel; protected _drag: boolean; protected _drag_start_pos: { [x: string]: [number, number]; }; protected _drag_start_sizes: { [x: string]: [number, number]; }; protected first_drag_move: boolean; protected _node_current_dx: number; protected _node_current_dy: number; protected _tied_to_nodes: boolean; protected _attached_node: Class_NodeBase[]; protected _attached_container: Class_NodeBase[]; protected _center_anchor_w: number | undefined; protected _center_anchor_h: number | undefined; protected _center_x: number | undefined; protected _center_y: number | undefined; /** * os#1383 — Poussée D'AFFICHAGE en cours sur `position_y` (anti-chevauchement et clamp haut * du mode « échelle adaptée », cf. `resolveScaleAdaptedOverlaps`). Elle ne fait pas partie de * la vérité du nœud (le centre) : toute CAPTURE du centre depuis le coin la défait d'abord, * et toute dérivation du coin depuis le centre l'annule. Sans ce registre, un nœud poussé dans * une vue puis masqué dans la suivante gardait son coin poussé, que `settleCenterAnchor` * (bascule de mode, sur TOUS les nœuds) ou la branche « taille inchangée » de * `anchorByCenterIfResized` committaient dans le centre — du maître, donc de toutes les vues * légères. Mesuré sur CARTOFOB : Prélèvements (Douglas) remonté à y = −793. */ protected _display_shift_y: number; protected _display_base_y: number | undefined; /** Décale le coin pour l'AFFICHAGE seulement : sera défait avant toute capture du centre. */ pushDisplayShiftY(dy: number): void; /** Ramène le coin là où il était avant les poussées d'affichage. */ undoDisplayShift(): void; /** Oublie les poussées sans toucher au coin : il vient d'être redérivé, ou déplacé exprès. */ clearDisplayShift(): void; _prop_center_ref: number | undefined; protected class_name: string; constructor(id: string, name: string, drawing_area: Class_DrawingArea, default_style: Class_ElementStyle, parent_svg?: string); protected _copyFrom(_: Class_NodeBase): void; drawAsSelected(): void; protected drawElements(): void; drawIcon(): void; drawShape(): void; drawNameLabel(): void; drawFO(): void; useDefaultStyle(): void; eventMouseOver(event: React.MouseEvent): void; setInputLabelVisible(initialValue?: string): void; setInputLabelInvisible(): void; shiftVertically(shift: number): void; protected _draw(): void; protected _initDraw(): void; /** * #377 — objet de dessin du LIBELLÉ de cette forme. Exposé pour que le label de valeur puisse * connaître la hauteur déjà consommée par le libellé (règle « libellé prioritaire, puis * valeur »). Lecture seule : l'instance est créée au constructeur et ne change jamais. */ get name_label_drawer(): NodeDrawNameLabel; getShapeWidthToUse(): number; getShapeHeightToUse(): number; /** * Seuil d'affichage du label (#seuil px). true = le label peut s'afficher. * Base : pas de seuil (les sous-classes nœud/stock connaissent leur valeur et * redéfinissent ce getter en s'appuyant sur drawing_area.{node,stock}LabelPassesThreshold). */ get is_above_label_threshold(): boolean; /** * #1230 — Mode coordonnées absolues : garde le CENTRE du nœud fixe quand sa * taille de rendu change pour une raison automatique (échelle globale des flux, * valeur, bascule de vue/datatag). À appeler une fois par cycle de dessin, avant * que le nœud ne soit dessiné (cf. `Class_NodePositioning.anchorAbsoluteNodesByCenter`). * * Compare la taille courante à l'ancrage mémorisé : si elle a changé, décale le * coin haut-gauche (position_x/y) d'une demi-variation pour que le centre ne * bouge pas, puis ré-ancre. Idempotent : sans changement de taille, no-op. Au * tout premier appel (ancrage undefined) on ne fait que mémoriser — la position * chargée/initiale est donc préservée telle quelle. */ anchorByCenterIfResized(): void; /** * #1230 — Re-cale l'ancrage du centre sur la taille de rendu courante. À appeler * après tout redimensionnement EXPLICITE par l'utilisateur (poignées de resize, * qui gardent volontairement le bord opposé fixe et déplacent donc le centre) et * au passage en mode absolu, pour que `anchorByCenterIfResized()` ne « re-centre » * pas par-dessus l'action voulue au prochain dessin complet. */ settleCenterAnchor(): void; /** * #1231 (1.1.5) — Dérive le CENTRE depuis le coin courant (position_x/y) + la taille * de rendu courante. C'est aussi la conversion coin→centre des fichiers < 1.1.5 * (appelée au 1er draw, datatag du save restauré → exact en mode absolu). */ captureCenterFromCorner(): void; /** * #1231 (1.1.5) — Dérive le COIN (position_x/y) depuis le centre stocké + la taille * de rendu courante. No-op si le centre n'a pas encore été initialisé. Deviendra le * calcul autoritaire au dessin une fois le flip effectué. */ applyCenterToCorner(): void; /** * #1231 (1.1.5) — Dérivation FORCÉE du coin depuis le centre, en resynchronisant l'ancre * de taille. À appeler en SORTIE d'un mode d'affichage (proportionnel / échelle) pour * ramener le nœud à sa vraie position absolue (centre stocké) au lieu de figer le coin * d'affichage (comprimé / rescalé). No-op si le centre n'est pas encore initialisé. */ forceDeriveFromCenter(): void; /** * #1231 (1.1.5) — Translation RIGIDE du centre stocké (et de la référence proportionnelle) * du même vecteur qu'un décalage de position_x/y. À appeler quand le coin est translaté en * bloc SANS passer par les gestes habituels (typiquement `DrawingArea.recenter()`), sinon le * centre persisté (`centerForPersistence`) reste périmé et les nœuds « reviennent » à leur * place pré-décalage au rechargement. No-op sur les champs non encore initialisés. */ translateStoredCenter(dx: number, dy: number): void; /** #1231 (1.1.5) — Centre stocké (lecture), undefined si pas encore initialisé. */ get center_x(): number | undefined; get center_y(): number | undefined; /** * #1231 (1.1.5) — Centre à PERSISTER (format 1.1.5 : x/y du JSON = centre). Si le centre * n'a pas encore été initialisé (nœud jamais dessiné), on le dérive du coin courant. */ centerForPersistence(): { x: number; y: number; }; /** * #1231 (1.1.5) — Chargement d'un fichier ≥ 1.1.5 (x/y = centre) : pose le centre comme * vérité. On dérive aussi un coin provisoire cohérent ; le 1er draw recalculera le coin * exact depuis le centre (applyCenterToCorner via anchorByCenterIfResized). */ setStoredCenter(x: number, y: number): void; /** * #1231 — Mode proportionnel : mémorise le centre vertical courant comme référence. * Appelé par `captureProportionalReference` (entrée du mode, après un drag, changement * de vue). Le replacement ultérieur scale ce centre autour de la médiane par le facteur f. */ captureProportionalCenterRef(): void; /** * #1231 — Mode proportionnel : replace le centre vertical à * `median + (center_ref − median) × f` (compression/dilatation autour de la médiane, * centre de gravité fixe). No-op si aucune référence n'a été capturée. */ applyProportionalCompression(median_y: number, factor: number): void; /** * Taille (w, h) du bbox des enfants attachés visibles, marges incluses, * calculée dynamiquement quand ce nœud est un cadre tied. Sans ça, à la * sortie d'un mode englobant le parent resterait figé à la taille bumpée * via `shape_min_*` (l'ancien `expandToContainAttachedNodes` écrivait dans * shape_min_*, ce qui ne se restaurait pas tout seul). Ici on lit * l'enveloppe à la volée — comme `is_visible_for_sizing_of(node)` le fait * pour les enfants masqués par container_mode. */ protected _envelopeSize(): { w: number; h: number; }; protected _orderD3Elements(): void; protected onSingleLMBClick(event: React.MouseEvent): void; protected onDoubleLMBClick(event: React.MouseEvent): void; /** P2 — édition du label de VALEUR ; surchargé par les feuilles qui en ont un. */ protected openValueLabelEditor(): void; drawSelectedLabelHandles(prefix: 'name_label' | 'value_label' | 'icon' | null): void; /** * OS#1259 — Fait SUIVRE les cadres qui contiennent cet élément quand il bouge : * chaque cadre tied grandit pour continuer à contenir ses membres, de proche en * proche jusqu'aux cadres emboîtés les plus englobants. * * S'appuie sur `growFrameToContainMembers` (union sur les QUATRE côtés) et NON * sur `expandToContainAttachedNodes`, qui ne déplace le coin que vers le haut et * la gauche : un membre traîné vers le BAS ou la DROITE sortait du cadre sans * que celui-ci suive — le cas le plus courant, puisqu'on descend un nœud sous * ses voisins bien plus souvent qu'on ne le remonte au-dessus. */ growEnclosingFrames(visited?: Set): void; protected eventMouseDrag(event: d3.D3DragEvent): void; protected eventMouseDragStart(event: d3.D3DragEvent): void; eventMouseDragEnd(event: d3.D3DragEvent): void; protected eventMaintainedClick(event: React.MouseEvent): void; protected eventSimpleRMBClick(event: React.MouseEvent): void; eventMouseMove(event: React.MouseEvent): void; eventMouseOut(event: React.MouseEvent): void; get name(): string; set name(_: string); /** Map complète { langue -> nom } (persistance / copie). */ get name_lang_map(): Type_LangMap; set name_lang_map(_: Type_LangMap); get name_label(): string; get name_label_custom(): boolean; set name_label_custom(_: boolean); get name_label_effective(): string; get name_label_effective_editable(): string; protected resolveTagLabel(): string; protected resolveTemplateLabel(): string; /** * Résolution d'UN jeton. Renvoie null pour un jeton inconnu (laissé tel quel * dans le texte). Surchargé par les sous-classes, qui délèguent ici pour les * jetons universels. */ protected resolveTemplateToken(token: string): string | null; protected resolveAncestorLabel(): string; get attached_container(): Class_NodeBase[]; get attached_node(): Class_NodeBase[]; get tied_to_nodes(): boolean; set tied_to_nodes(b: boolean); attachNodeToCont(node: Class_NodeBase): void; attachContToNode(node: Class_NodeBase): void; dettachNodeFromCont(node: Class_NodeBase): void; dettachContFromNode(node: Class_NodeBase): void; computeSizeAndPositionFromAttachedNodes(): void; expandToContainAttachedNodes(): void; /** * OS#1259 — Fait GRANDIR le cadre (grow-only, TOUS les côtés) pour contenir ses * membres. `expandToContainAttachedNodes` ne bougeait le coin que vers le * haut/gauche, et la taille via `_envelopeSize` = ÉTALEMENT des membres (donc * constante pour un membre unique qui s'éloigne) : rien n'étendait le cadre * vers la droite/bas. Ici on prend l'UNION de la boîte de contenu courante et * de la bbox des membres, et on écrit position + `shape_min_*` (donc ça * persiste au save). `settleCenterAnchor()` recale l'ancre centre/coin (#1231) * pour que le prochain dessin ne re-centre pas par-dessus. Grow-only : ne * rétrécit jamais (un membre ramené à l'intérieur ne réduit pas le cadre). */ growFrameToContainMembers(): void; /** * OS#1259 (clamp) — Pendant qu'on REDIMENSIONNE le cadre, contraint chaque * membre à rester dans sa boîte de CONTENU : si un membre dépasse (cadre * rétréci sous sa taille), on rétrécit sa taille pour qu'il tienne, puis on * repousse sa position dans les bornes. Inverse de growFrameToContainMembers. * Ne touche pas un membre qui tient déjà (« dès que ça dépasse »). Un membre * qui est lui-même un cadre tied ne descend pas sous l'enveloppe de SES * membres (getShapeWidthToUse = max(shape_min, enveloppe)). */ clampMembersToFrame(): void; /** * #363 — Recale le bord GAUCHE du cadre sur l'enveloppe LABELS INCLUS de ses * membres. La TAILLE du cadre est déjà label-incluse * (`_envelopeSize` lit le `getBBox()` du SVG), mais son coin ne l'était qu'en * fin de drag (`expandToContainAttachedNodes`) : un fichier dont les libellés * de nœuds débordent à gauche (`name_label_horiz: 'left'` + * `name_label_inside_horiz: false`) s'ouvrait donc avec un cadre de la bonne * largeur mais TRANSLATÉ vers la droite du débord — il entourait les formes, * pas les libellés — jusqu'au premier clic sur un membre. * * HORIZONTAL seulement, à dessein : le bord haut d'un cadre appartient au mode * de positionnement actif, qui le réécrit à chaque dessin (empilement de * colonne par `anchorParametricNodesToAbsolute`, ancre de centre par * `anchorByCenterIfResized`). Le corriger ici le ferait osciller d'un dessin au * suivant, et comme le ré-empilement des enfants (`restackContainerChildren`, * Phase C de `recomputeParametricLayout`) s'ancre sur le haut du cadre, chaque * passe ferait remonter la pile d'un débord de libellé — dérive sans fin. Rien, * en revanche, ne réécrit le x d'un cadre : la correction y est stable. * * À appeler APRÈS le dessin : le débord des libellés n'entre dans le * `getBBox()` qu'une fois ceux-ci rendus. * * @returns true si le coin a bougé — au caller de redessiner le cadre. */ refitTiedFrameToLabels(): boolean; reanchorTiedFrame(): void; /** * os#671 / os#1347 — hook de CONTRAINTE fourni par le modèle au geste de drag * (smart guides) : sur quels axes la position déposée fait-elle autorité — * donc mérite un alignement magnétique ? Base (conteneurs, zones, légende…) : * position libre sur les deux axes. `Class_NodeElement` raffine selon la mise * en page pilotée par les données (cf. Node.getFreeDragSnapAxes). */ getFreeDragSnapAxes(): { x: boolean; y: boolean; }; setDragStartPositions(positions: { [x: string]: [number, number]; }): void; getDragStartPositions(): { [x: string]: [number, number]; }; setDragStartSizes(sizes: { [x: string]: [number, number]; }): void; getDragStartSizes(): { [x: string]: [number, number]; }; setDragState(drag: boolean): void; getDragState(): boolean; setFirstDragMove(value: boolean): void; getFirstDragMove(): boolean; updateNodeCurrentDelta(dx: number, dy: number): void; resetNodeCurrentDelta(): void; getNodeCurrentDeltas(): { dx: number; dy: number; }; /** * Activate the control points alignement guide * * @private * @return {*} * @memberof Class_ContainerElement */ protected dragHandleStart(): () => void; /** * Deactivate the control points alignement guide * @private * @return {*} * @memberof Class_ContainerElement */ protected dragHandleEnd(): () => void; /** * Event when we drag the top handle * * @private * @return {*} * @memberof Class_ContainerElement */ protected dragTopHandler(): (event: d3.D3DragEvent) => void; /** * Event when we drag the bottom handle * * @private * @return {*} * @memberof Class_ContainerElement */ protected dragBottomHandler(): (event: d3.D3DragEvent) => void; /** * Event when we drag the left handle * * @private * @return {*} * @memberof Class_ContainerElement */ protected dragLeftHandler(): (event: d3.D3DragEvent) => void; /** * Event when we drag the right handle * * @private * @return {*} * @memberof Class_ContainerElement */ protected dragRightHandler(): (event: d3.D3DragEvent) => void; private computeTopHandlerPos; private computeBottomHandlerPos; private computeLeftHandlerPos; private computeRightHandlerPos; /** * OS#1276b — garantit que les 4 offsets d'extrémité (shape_line_x1…y2) décrivent * un vrai segment. Idempotent : si le segment est déjà non dégénéré (A ≠ B) on ne * touche à rien. S'il est dégénéré (cas d'un ancien fichier « diagonale de boîte » * où les offsets valent 0, ou d'une création qui n'a posé que shape_line_flip), on * les dérive de la boîte (w = shape_min_width, h = shape_min_height) et du sens * historique shape_line_flip : * flip=false → A=(0,0), B=(w,h) « \ » * flip=true → A=(0,h), B=(w,0) « / » * La boîte et position_x/y restent inchangées (les offsets sont déjà normalisés * min=0). Appelé au chargement (ContainerPersistence.fromJSON) et à la création. */ ensureLineEndpoints(): void; /** * OS#1276b — drag d'une extrémité de ligne libre. Recalcule la position monde de * l'extrémité déplacée (offset local + event.dx/dy), puis RENORMALISE tout le * segment pour que min(x1,x2)=0 et min(y1,y2)=0 : le décalage est absorbé par * position_x/y (le coin haut-gauche suit le min), les 4 offsets et la boîte dérivée * (shape_min_width/height) sont réécrits. Ainsi tous les invariants « position = * coin haut-gauche, boîte = max des offsets » restent vrais (resize, hit, ordre-Z). */ protected dragEndpointHandler(which: 'a' | 'b'): (event: d3.D3DragEvent) => void; private _ensureLineEndpointHandlers; /** * Draw all control points * * @private * @memberof Class_ContainerElement */ drawDragHandlers(): void; applyPosition(): void; /** * Compute the enclosing bounding box of a list of nodes. Prefers the * SVG getBBox so the label overhang is naturally included in the * envelope, and falls back to the logical geometry (position + shape * size) when the node has not been rendered yet or the SVG bbox is * empty. The SVG bbox is local to the node's g element, so it stays * valid even if the g's transform has not yet been flushed — we * combine it with the fresh node.position_x/y rather than the SVG's * absolute coordinates. * Returns null if no node is visible. * Shared utility used by containers (TextZone) and aggregation container mode. */ protected _computeEnvelopeBBox(nodes: Class_NodeBase[]): { min_x: number; min_y: number; max_x: number; max_y: number; } | null; /** * Apply the given envelope bbox as this node's position and min size, * padded by the shape_margin_* attributes. */ protected _applyEnvelopeBBox(bbox: { min_x: number; min_y: number; max_x: number; max_y: number; }): void; get position_u(): number; set position_u(_: number); get position_v(): number; set position_v(_: number); get selected_elements_list(): Class_NodeBase[]; set_contextualized_element(_element: Class_NodeBase): void; }