import { TFunction, i18n } from 'i18next'; import * as d3 from '../d3Modules'; import { StepType } from '@reactour/tour'; import { Class_GuidedTour } from './GuidedTour'; import { CreateToastFnReturn } from '@chakra-ui/react'; import { Class_MenuConfig } from '../types/MenuConfig'; import { Type_JSON } from './Utils'; import { PublishOptions } from './PublishOptions'; import { Class_ApplicationHistory } from './ApplicationHistory'; import { ViewsReader } from './ViewsReader'; import type { Type_ViewEntry } from './ViewsQuery'; import { Class_IconLibrary } from '../css/IconLibrairie'; import { Class_DrawingArea } from './DrawingArea'; import { Type_DocMarkdownMap } from '../Persistence/persistenceMigrations'; import type { Class_NodeElement } from '../Elements/Node'; import type { Class_LinkElement } from '../Elements/Link'; export type Type_TextForToastPromise = { success?: { title?: string; desc?: string; }; error?: { title?: string; desc?: string; }; loading?: { title?: string; desc?: string; }; }; export type MenuColorPickerProps = { initialColor: string; functionOnBlur: (x: string) => void; isDisabled?: boolean; textDisabled?: string; }; /** Un diagramme proposé dans la pop-up de présentation d'un élément (bouton + * rendu). Fourni par OS+ via `Class_ApplicationData.presentation_diagrams_for`. */ export type Type_PresentationDiagram = { /** Id stable ('unit' | 'donut' | 'bar'). */ id: string; /** Libellé du bouton (déjà traduit). */ label: string; /** Icône du bouton (au-dessus du libellé, comme les onglets de config). */ icon?: React.ReactNode; /** Dessine le diagramme dans le conteneur DOM ; rend un nettoyage optionnel. */ render: (container: HTMLElement) => (() => void) | void; }; /** * sa#456 — PAGE PUBLIÉE d'où vient le diagramme affiché, quand il a été ouvert par * `?url=` sur une adresse du parc que le serveur a reconnue. * * C'est l'autre visage d'une provenance : `Type_SankeythequeOrigin` désignait un * fichier du DÉPÔT source, elle ne savait pas dire « la page / du * parc, fichier X.json.gz ». `data_file` est le nom que le MANIFESTE de publication * déclare pour cette adresse (jamais celui de l'URL, le parc servant `X.json.gz`, * `X.json`, `X.gz` et jusqu'à `X` tout court) : c'est lui, et lui seul, que la mise * à jour remplace — une page multi-diagrammes voit corriger celui qu'on a ouvert. * * `kind` dit la NATURE de la source du portfolio, donc celle du geste de mise à * jour : 'mfadata' (le portfolio vient d'un dépôt : commit + page, geste * historique) ou 'workbook' (il est rendu depuis un classeur : brique de * bibliothèque + page, cf. server/publish_provenance.py). */ export type Type_PublicationOrigin = { page_url: string; slug: string; leaf: string; data_file: string; /** 'mfadata' (portfolio issu d'un dépôt) ou 'workbook' (rendu depuis un classeur). */ kind: string; }; /** * Provenance d'un diagramme ouvert depuis une galerie réenregistrable : chemin du * modèle dans l'index, relatif à la racine de sa source, et nom affiché. * `source` désigne la galerie donc le dépôt écrit — 'mfadata' = la sankeythèque * (études), 'sankeydata' = les modèles. Le couple (source, chemin) est le seul * qui compte côté serveur : le chemin doit être exactement celui de l'index de * cette source, lequel fait liste blanche d'écriture. * * sa#456 — `file_path` peut être VIDE : une page publiée dont la source n'est pas * un fichier de dépôt (portfolio rendu depuis un classeur, ou étude absente de * l'index curaté) a bien une provenance, mais rien à réenregistrer dans un dépôt. * Le volet dépôt du dialogue se ferme alors, et `source` n'est pas consulté ; * `publication.kind` porte la vérité de ce qui met la page à jour. */ export type Type_SankeythequeOrigin = { file_path: string; title: string; source: 'mfadata' | 'sankeydata'; publication?: Type_PublicationOrigin; }; /** * OS#85 — Une FEUILLE du document : un AUTRE diagramme, indépendant, dans le même * fichier (règle des deux niveaux, cf. NOTE-CONSTRUCTEUR-DE-SITE.md §6) : une VUE * suit les données de son diagramme (heredited_attr), une FEUILLE porte d'autres * données et vit sa vie. Mécanisme FRÈRE des vues mais au niveau DOCUMENT. */ export type Type_SheetEntry = { /** Nom affiché dans l'onglet (bas de la grande zone). */ name: string; /** * Snapshot gzip du diagramme complet de la feuille (JSON du document SANS la clé * racine `sheets` — cf. `_currentDiagramAsSheetJSON`). `undefined` pour la feuille * COURANTE : son contenu est l'état vivant (drawing_area + vues), rafraîchi ici à * chaque bascule / sauvegarde. */ json?: Uint8Array; }; /** * Association du document ouvert à sa BRIQUE de bibliothèque (sa#399) : id du * projet côté serveur (server/library.py) et chemin du fichier dans le manifeste * des versions. PERSISTÉE dans le JSON du diagramme (clé racine `library_ref`) * pour survivre au fichier : rouvrir le JSON ré-associe le document à sa brique, * et « enregistrer dans ma bibliothèque » y dépose la version suivante au lieu * d'en créer une nouvelle. Un fichier SANS cette clé = nouvelle brique. */ export type Type_LibraryRef = { project_id: number; path: string; }; /** Relecture défensive de la clé racine `library_ref` : absente ou malformée => null. */ export declare const parseLibraryRef: (value: unknown) => Type_LibraryRef | null; /** * Class that contains all elements to make the application work * * @class Class_ApplicationData */ export declare class Class_ApplicationData { static readonly export_edge_padding: number; protected _has_sankey_dev: boolean; protected _has_sankey_plus: boolean; protected _has_sankey_afm: boolean; readonly publish_options: PublishOptions; get has_sankey_dev(): boolean; set has_sankey_dev(_: boolean); get has_sankey_plus(): boolean; set has_sankey_plus(_: boolean); get has_sankey_afm(): boolean; set has_sankey_afm(_: boolean); /** * Libellé de l'édition active, affiché en pastille à droite du logo de la barre * du haut — le wordmark seul ne dit pas quelle édition tourne. Null = rien à * afficher (OpenSankey libre : le logo suffit). Surchargé en OpenSankey+. */ get edition_badge(): string | null; /** True hors mode publish, ou en publish si l'option `editable` est activée. */ get is_editable(): boolean; /** * os#1365 — ARBITRE UNIQUE entre les deux sélecteurs de la topbar : la navigation entre * vues (BannerViewNavOSP) rend le sélecteur quand ce drapeau est vrai, le sélecteur de * view tags (BannerViewTagTopbar) quand il est faux. Les deux bannières lisent CETTE * propriété et elle seule : conditions complémentaires, donc jamais deux sélecteurs à * l'écran, jamais zéro. * * Le critère est la PRÉSENCE DE VUES, pas la licence. Dès qu'un view tag a engendré des * vues (préfixe `vt____`), ce sont les vues qui pilotent — une seule source * de vérité. Sans vues, le sélecteur de view tags reste : c'est la seule UI du diagramme, * le retirer le rendrait inutilisable. * * La version précédente valait `has_sankey_plus` en OpenSankey+, ce qui divergeait de la * garde `has_views` de BannerViewNavOSP et produisait DEUX défauts symétriques : * - éditeur, vues présentes sans licence plus → les DEUX sélecteurs (le doublon CARTOFOB) ; * - viewer d'une publication, où `has_sankey_plus` est vrai par `is_static` : sans vues, * AUCUN sélecteur, le diagramme publié perdait sa seule UI de filtrage. * * Le mécanisme de visibilité, lui, reste en OS (Sankey.view_taggs / Node) : c'est ici une * question d'AFFICHAGE. */ get views_replace_viewtag_topbar(): boolean; /** * sa#283 — Vues contextuelles : slot OPTIONNEL enregistré par la couche OSP (pattern * d'enregistrement, AUCUN import runtime OS → OSP — piège TDZ Element→Handler). Appelé * par les méthodes de sélection des groupes de tags (TagGroup.selectTagsFromId / * selectTagsFromIds, Tag.toogleSelected) juste APRÈS le basculement des tags et AVANT * le redraw, pour que l'overlay d'attributs contextuels parte dans le dessin. Null en * OS base : la feature vit entièrement en OpenSankey+. */ after_tag_selection_change: (() => void) | null; protected _publish_state_applied_once: boolean; /** * os#1372 — Compteur de dessins COMPLETS, incrémenté par `Class_DrawingArea.draw()`. * * Sert à savoir si un geste a DÉJÀ redessiné avant d'en déclencher un de plus. Mesuré sur * CARTOFOB : une bascule de dataTag enchaînait DEUX dessins complets (celui de * `selectTagsFromId` → `updateTagsReferences`, puis celui de fin d'`applyPublishStateOptions`) * et une bascule de vue heavy QUATRE. Supprimer le seul dessin redondant du chemin dataTag * ramène le geste de 1 564 ms à 878 ms (A/B alterné, médianes sur 4 tours). * * Vit sur l'application et non sur la zone de dessin : celle-ci est REMPLACÉE en cours de * geste sur le chemin heavy (`extractViewFromJSON` → `replaceDrawingArea`), un compteur porté * par elle repartirait donc de zéro au milieu du geste. */ protected _draw_epoch: number; get draw_epoch(): number; /** Appelé par `Class_DrawingArea.draw()` — ne pas appeler ailleurs. */ notifyFullDraw(): void; /** * os#1372 — Époque de référence posée par un SURCHARGEUR d'`applyPublishStateOptions` avant * son propre travail (OSP ouvre la vue demandée AVANT d'appeler `super`). Sans elle, la garde * du dessin final ne verrait pas le dessin déclenché par cette ouverture et en ajouterait un * second. Consommée par la méthode de base au premier usage. */ protected _publish_apply_epoch: number | null; /** À appeler en tête d'une surcharge d'`applyPublishStateOptions`, avant tout dessin. */ protected markPublishApplyStart(): void; /** * os#1377 — Vrai pendant la lecture d'un fichier qui se terminera par un dessin complet * (`fromJSON(..., draw = true)`). Ce que la lecture dessine d'elle-même serait alors refait * à l'identique : `ViewsReader.viewsFromJSON` s'en abstient, ce qui économise une passe * ENTIÈRE au chargement — 70 dessins de flux sur 211 pour CARTOFOB, sur la géométrie de la * vue enregistrée que les options de publication remplacent aussitôt après. * * Faux hors chargement et pour un `fromJSON(..., draw = false)` (réconciliation, tests de * corpus) : là, le dessin de la lecture est le seul, et rien ne change. */ protected _from_json_will_draw: boolean; get from_json_will_draw(): boolean; createNewMenuConfiguration(toast?: CreateToastFnReturn | null): Class_MenuConfig; createNewDrawingArea(id?: string): Class_DrawingArea; /** Load a drawing area from JSON. Override in subclasses to use a subclass-specific persistence layer. */ loadDrawingAreaFromJSON(drawing_area: Class_DrawingArea, json_object: Type_JSON): void; /** Replace the current drawing_area with a freshly-built one. * Unmounts the previous DA's DOM (if attached) and swaps the internal * reference. Callers keep using `app_data.drawing_area` (getter) so no * downstream binding needs updating. */ replaceDrawingArea(new_drawing_area: Class_DrawingArea): void; createNewIconLibrary(): Class_IconLibrary; version: string; fit_screen: boolean; static_path: string; options: { [_: string]: boolean | string; }; data_var_to_update: string[]; /** Called after applying a layout from an external source. * tmp_DA is the already-converted source DrawingArea. * json is the raw source file JSON (null for view sources). * mode overrides data_var_to_update when provided (e.g. when called from App.tsx with all attrs). */ post_apply_layout_callback?: (tmp_DA: Class_DrawingArea, json: Type_JSON | null, mode?: string[]) => void; /** Hook injecté par OS+ (cf. ModalUnitarySankeyOSP) : dessine le sankey unitaire * focalisé sur `node` dans le conteneur DOM `container_selector`, EN PLUS du * diagramme principal. Retourne un handle pour le redessiner (resize) et le * nettoyer. Alimente l'onglet « Sankey unitaire » du tooltip de nœud * (NodeTooltip). Absent hors OS+. */ draw_unitary_in_container?: (node: Class_NodeElement, container_selector: string) => { redraw: () => void; cleanup: () => void; } | void; /** Hook injecté par OS+ (cf. ModalUnitarySankeyOSP) : dessine le GRAPHIQUE * D'ANALYSE (couronne / histogramme) décrit par l'attribut analysis_descriptor * de l'élément (nœud OU flux) dans le conteneur DOM `container_selector`. * Alimente l'onglet « Analyse » des tooltips de nœud et de flux quand * surfaces.tooltip est activé (OS#1278). Absent hors OS+. */ draw_analysis_in_container?: (element: Class_NodeElement | Class_LinkElement, container_selector: string) => { redraw: () => void; cleanup: () => void; } | void; /** Hook injecté par OS+ : dessine le nœud EN CAMEMBERT (surface on_node, OS#1278) * dans le groupe SVG `group_el` du nœud, aux dimensions passées. Utilisé par * NodeDrawShape quand le descripteur du nœud a surfaces.on_node. Couleurs du * diagramme (le graphique fait partie du langage visuel). Absent hors OS+. */ draw_node_analysis_overlay?: (node: Class_NodeElement, group_el: SVGGElement, width: number, height: number) => boolean; /** Hook injecté par OS+ : DIAGRAMMES proposés pour un élément dans la pop-up de * présentation (colonne de boutons Unit. / Couronne / Barres). Chacun sait se * dessiner dans un conteneur DOM. Absent hors OS+ (pas de colonne de diagrammes). */ presentation_diagrams_for?: (element: Class_NodeElement | Class_LinkElement) => Type_PresentationDiagram[]; protected _waiting_processes: { [id: string]: NodeJS.Timeout; }; protected _waiting_time_for_processes: number; protected _file_name: string; protected _static_diagram_file: string | null; protected _sankeytheque_origin: Type_SankeythequeOrigin | null; protected _documentation_markdown: Type_DocMarkdownMap; protected _documentation_images: { [id: string]: string; }; protected _publish_settings: Type_JSON; protected _library_ref: Type_LibraryRef | null; /** * Drawing area * * @protected * @type {Class_DrawingArea} * @memberof Class_ApplicationData */ protected _drawing_area: Class_DrawingArea; /** DA du Sankey MAÎTRE (référence de mise en page) quand le fichier porte des vues. */ protected _master_drawing_area: Class_DrawingArea | undefined; get master_drawing_area(): Class_DrawingArea | undefined; set master_drawing_area(master: Class_DrawingArea | undefined); /** Vues enregistrées, indexées par id : snapshot gzip + concept unifié vue ⊕ viewtag. */ protected _views: { [id: string]: Type_ViewEntry; }; get views_dict(): { [id: string]: Type_ViewEntry; }; /** Ordre des vues (le maître n'y figure pas). Muté en place par les méthodes d'ordre. */ protected _views_order: string[]; get views_order(): string[]; protected _show_master_in_views: boolean; get show_master_in_views(): boolean; set show_master_in_views(v: boolean); protected _master_view_name: string; get master_view_name(): string; set master_view_name(v: string); protected _keep_camera_across_views: boolean; get keep_camera_across_views(): boolean; set keep_camera_across_views(v: boolean); protected _publish_view_label_filter: string | null; get publish_view_label_filter(): string | null; set publish_view_label_filter(v: string | null); protected _publish_view_labels: string[]; get publish_view_labels(): string[]; set publish_view_labels(v: string[]); protected _current_view_id: string; get current_view_id(): string; set current_view_id(v: string); /** Feuilles du document, indexées par id (snapshot gzip sauf feuille courante). */ protected _sheets: { [id: string]: Type_SheetEntry; }; get sheets_dict(): { [id: string]: Type_SheetEntry; }; /** Ordre d'affichage des onglets de feuilles. Vide = document mono-feuille historique. */ protected _sheets_order: string[]; get sheets_order(): string[]; /** Id de la feuille courante ('' tant que le document n'a pas de feuilles). */ protected _current_sheet_id: string; protected _loading_into_sheet: boolean; get current_sheet_id(): string; /** True dès que le document porte des feuilles nommées (au moins une entrée). */ get has_sheets(): boolean; protected _views_reader: ViewsReader; protected instanciateViewsReader(): ViewsReader; /** * os#1369 — Exécute un geste LOURD d'interface (filtrage par dataTag, et tout ce qui * redessine le diagramme entier) en cédant d'abord la main au navigateur, voile et sillon * posés. Suite directe d'os#1368 : la cause est la même — le travail est synchrone, donc * un indicateur posé juste avant ne serait JAMAIS peint —, et l'ordonnanceur est le MÊME * instance que celui de la bascule de vue, pour qu'il n'y ait qu'un voile à l'écran et que * l'ordre soit préservé entre les deux familles de gestes. * * `then` court APRÈS le travail, cédé ou non : les hôtes y rafraîchissent leurs composants, * qui sinon liraient l'état d'avant, une frame trop tôt. * * Réservé aux gestes d'UTILISATEUR. Les chemins programmatiques — exports, options de * publication, suites de tests, qui lisent l'état au retour — appellent le travail * directement et restent strictement synchrones, comme `setCurrentView` face à * `requestViewChange`. */ runHeavyGesture(work: () => void, then?: () => void): void; /** * History of all actions * * @protected * @type {Class_ApplicationHistory} * @memberof Class_ApplicationData */ protected _history?: Class_ApplicationHistory; protected _clipboard_node_ids: string[]; /** * Configuration Menu * * @protected * @type {Class_MenuConfig} * @memberof Class_ApplicationData */ protected _menu_configuration?: Class_MenuConfig; /** * Librairie containing icon for the app * * @protected * @type {Class_MenuConfig} * @memberof Class_ApplicationData */ protected _icon_library: Class_IconLibrary; /** * All possible attr to update in copyFrom * @protected * @type {string[]} * @memberof Class_ApplicationData */ protected get _transform_layout_all_attr(): string[]; protected _t: TFunction; protected _i18n: i18n; /** * Path to OpenSankey logo * @private * @type {string} * @memberof Class_ApplicationData */ private _logo_opensankey; /** * Path to Terriflux logo * @private * @type {string} * @memberof Class_ApplicationData */ private _logo_terriflux; /** * Width of logo * @private * @type {number} * @memberof Class_ApplicationData */ private _logo_width; /** * Application name * @private * @type {string} * @memberof Class_ApplicationData */ private _app_name; /** * Path prefix for backend server requests * @private * @type {string} * @memberof Class_ApplicationData */ private _url_prefix; /** * Varaible to save language selected * @private * @type {(string | undefined)} * @memberof Class_ApplicationData */ private _language?; /** * Ref to launch _function_on_wait & create a _toast with a spinner to show we have to wait * @private * @memberof Class_ApplicationData */ protected _toast: CreateToastFnReturn | null; /** * Queue of waiting processes for toast * @private * @type {string[]} * @memberof Class_ApplicationData */ private _toast_processes; /** * Force bypassing waiting toast * @private * @type {boolean} * @memberof Class_ApplicationData */ private _toast_bypass; /** * Guided visite steps to show app * @private * @type {StepType[]} * @memberof Class_ApplicationData */ private _steps; /** * #1255 — Scénario de la visite guidée (cf. Class_GuidedTour). Porte l'état du tour en cours : * gestes attendus, contenu de repli créé, nettoyage de fin. * @private * @memberof Class_ApplicationData */ private _guided_tour; /** * Session-only horizontal spacing for auto-layout. `null` = use style default. * Shared between the auto-layout context menu widget and the Excel import dialog. */ layout_h_spacing: number | null; /** * Session-only vertical spacing for auto-layout. `null` = use style default. * Shared between the auto-layout context menu widget and the Excel import dialog. */ layout_v_spacing: number | null; /** * Session-only placement mode for nodes without incoming flows (auto-layout). * 'before_neighbor' = one column before the earliest successor (default), * 'left_extremity' = pinned to the leftmost column (index 0). */ layout_sources_mode: 'before_neighbor' | 'left_extremity'; /** * Session-only placement mode for nodes without outgoing flows (auto-layout). * 'after_neighbor' = one column after the latest predecessor (default), * 'right_extremity' = pinned to the rightmost column. */ layout_sinks_mode: 'after_neighbor' | 'right_extremity'; /** * Session-only mode for the auto-layout: whether to minimize link crossings. * `true` = "Minimiser les croisements", `false` = "Centrer les nœuds". * Used by the Excel import dialog; the right-click menu exposes the choice via two buttons instead. */ layout_optimize_crossing: boolean; /** * sankeyapplication#153 — Recalcul automatique du statut recyclage après un déplacement * de nœud : un flux dont la cible ne se trouve plus à droite de sa source passe en * recyclage, et réciproquement. * * `true` (défaut) = « Recalcul auto », `false` = « Mise en page figée » — indispensable * sur un diagramme particulier dont l'utilisateur a réglé le recyclage à la main (le * verrou par flux reste de toute façon prioritaire sur la géométrie). * * Ne déplace AUCUN nœud : une mise en page manuelle survit au recalcul. * * PERSISTÉ (clé racine `layout_auto_recycling`, sérialisée seulement si `false`) — contrairement * aux autres `layout_*`, qui sont des réglages de session du dialogue de mise en page auto : * celui-ci décrit une propriété du diagramme (« ma disposition est libre, n'y touche pas »), et * repartait à `true` à chaque rechargement, rendant le choix inopérant. */ layout_auto_recycling: boolean; /** * Mode « afficher aussi les flux porteurs de données » : quand actif, EN PLUS de * la vue courante, on révèle les flux portant une valeur collectée saisie * (`Class_LinkElement.has_collected_data`) et leurs nœuds, tous niveaux * d'agrégation confondus (bypass des portes niveau/dimension). Union avec la vue * normale, pas un filtre. Vue d'exploration de session (non persistée). */ reveal_data_links: boolean; /** * Creates an instance of Class_ApplicationData. * @param {boolean} published_mode * @memberof Class_ApplicationData */ constructor(published_mode: boolean, options?: { [_: string]: boolean | string; }); /** * Reset drawing area -> clean data & undraw * @protected * @memberof Class_ApplicationData */ reset(_?: Type_JSON): void; /** * Reset data & delete application data in navigator cache * * @memberof Class_ApplicationData */ reinitialization(redraw?: boolean): void; /** * Save in JSON in browser cache * * /!\ Add to waiting spinner queue * * @memberof Class_ApplicationData */ saveInCache(): void; /** * Save as JSON in browser cache * @protected * @memberof Class_ApplicationData */ protected _saveInCache(): void; /** * sa#424 (lot 4) — DEMANDER AU NAVIGATEUR DE NE PAS ÉVINCER CE STOCKAGE. * * Ctrl+S écrit dans le stockage local, qui est par défaut « best-effort » : le * navigateur peut le purger sous pression de disque. `storage.persist()` le * fait passer en durable. * * Appelé au moment de l'enregistrement, PAS au démarrage : sous Firefox la * demande peut ouvrir une autorisation, et une invite surgissant à l'ouverture * de l'application serait incompréhensible — ici elle suit un geste délibéré * de l'utilisateur. Une seule tentative par session ; l'échec est sans * conséquence (on retombe sur le comportement d'avant). */ protected _persistence_requested: boolean; requestPersistentStorage(): void; /** * sa#424 (lot 4) — DATE DU DERNIER VRAI FICHIER ÉCRIT (JSON ou Excel). * * Le stockage de l'application n'est pas une sauvegarde : il est lié à un * navigateur, un profil et une origine, et part avec un nettoyage de données. * Cette date est la seule information qui prévienne d'une perte — d'où son * affichage à côté du bouton d'enregistrement, « jamais » compris. * * Les EXPORTS (PNG, PDF, SVG) ne comptent pas : ce sont des rendus figés, pas * des fichiers réouvrables — la distinction même qui sépare « Enregistrer * sous » d'« Exporter ». */ noteDocumentDownloaded(): void; get last_document_download(): Date | null; /** Horodatage du dernier enregistrement dans le stockage de l'application. */ get last_cache_save(): Date | null; protected _storedDate(key: string): Date | null; /** * save to JSON format * * /!\ Add to waiting spinner queue * * @memberof Class_ApplicationData */ saveToJSON(kwargs?: Type_JSON): void; /** * Hook ASYNCHRONE exécuté juste avant la sérialisation d'une sauvegarde JSON (dans le toast * d'attente, donc l'utilisateur voit le spinner). OS : rien. OSP y prépare les vignettes de * vues, dont la rasterisation est asynchrone alors que `_toJSON` est synchrone. */ protected beforeSaveToJSON(): Promise; /** * Save to JSON format * @protected * @memberof Class_ApplicationData */ protected _saveToJSON(kwargs?: Type_JSON): void; /** * Save as Excel format * * /!\ Add to waiting spinner queue * * @param {string} url_prefix * @param {string} [file_name='sankey'] * @memberof Class_ApplicationData */ saveToExcel(url_prefix: string, kwargs?: Type_JSON): void; /** * Save to Excel format * @protected * @param {string} url_prefix * @param {string} [file_name='sankey'] * @memberof Class_ApplicationData */ protected _saveToExcel(_name: string, _args?: Type_JSON): void; /** * Enregistre un geste LOURD (hiérarchies, pré-positionnement global, import/export…) * comme UNE seule entrée d'historique, par snapshots avant/après. * * Pourquoi ne pas rejouer l'action au redo, comme le fait executeWithUndo ? Parce que * fromJSON() passe par reset(), qui REMPLACE la drawing_area : après un undo, toute * référence capturée (nœud, tag group, drawing_area) pointe sur des instances mortes. * Restaurer l'état sérialisé des deux côtés évite complètement le problème. * * À réserver aux gestes qui mutent large : deux toJSON complets par appel. * `onRestore` sert à rafraîchir les menus après restauration. */ runWithSnapshotUndo(action: () => void, onRestore?: () => void): void; toJSON(kwargs?: Type_JSON): { [x: string]: string | number | boolean | string[] | Type_JSON; }; /** * Create json file that contains all application datas * @memberof Class_ApplicationData */ protected _toJSON(kwargs?: Type_JSON): { [x: string]: string | number | boolean | string[] | Type_JSON; }; /** * Reset value of drawing_area and substructur with data from JSON * then assign newly created drawing_area as Class_ApplicationData currentdrawing_area attribute * * /!\ Add to waiting spinner queue * * @param {Type_JSON} json_object * @memberof Class_ApplicationData */ fromJSON(json_object: Type_JSON, kwargs?: Type_JSON, draw?: boolean): void; /** * Overridable method to read JSON * @protected * @param {Type_JSON} json_object * @memberof Class_ApplicationData */ protected _fromJSON(json_object: Type_JSON, kwargs?: Type_JSON): void; /** * Sérialise la clé racine `sheets` : `{ current, order, entries: { id: { name, json? } } }`. * L'entrée de la feuille COURANTE n'a pas de `json` : la racine du fichier est son contenu * (un lecteur qui ignore `sheets` affiche donc la feuille courante, sans rien perdre). * * /!\ Chez OpenSankey+ (fichier avec vues), cette clé doit être écrite AVANT * encodeViewsAsDelta : la base du delta = la racine privée de `views`, STRICTEMENT * identique à l'écriture et à la lecture — sinon chaque vue hériterait de `sheets` * (même piège que les vignettes de vues, cf. ApplicationDataOSP._toJSON). */ protected sheetsToJSON(json_object: Type_JSON, kwargs?: Type_JSON): void; /** * Relit la clé racine `sheets`. Un fichier ancien (sans la clé) ou une clé malformée * chargent SANS BRUIT un document mono-feuille : l'état feuilles reste vide. */ protected sheetsFromJSON(json_object: Type_JSON): void; /** Nom par défaut d'une feuille (« Feuille N », traduit quand i18n est branché). */ protected _defaultSheetName(n: number): string; /** * Contenu de la feuille courante = sérialisation COMPLÈTE du document (diagramme + vues + * doc + réglages) SANS la clé racine `sheets` — c'est exactement ce que serait le fichier * si cette feuille était seule. */ protected _currentDiagramAsSheetJSON(): Type_JSON; /** * Charge le contenu d'une feuille via fromJSON en PRÉSERVANT l'état feuilles : fromJSON * passe par reset(), qui efface `_sheets` (sémantique « nouveau document ») et REMPLACE * la drawing_area — d'où le stash/restore, et l'usage exclusif des accesseurs ensuite. */ protected _loadSheetContent(json_object: Type_JSON, draw: boolean): void; /** * Enregistre le document courant comme première feuille si le document n'en a pas encore * (passage du monde mono-feuille au monde multi-feuilles). Idempotent. */ protected _ensureSheetsInitialized(): void; /** Rafraîchit le snapshot gzip de la feuille courante depuis l'état vivant. */ protected _snapshotCurrentSheet(): void; /** * Crée une NOUVELLE feuille (diagramme vierge, indépendant — règle des deux niveaux : * pour une lecture qui SUIT les données, c'est une vue qu'il faut créer) et bascule dessus. * @returns l'id de la feuille créée. */ createNewSheet(draw?: boolean): string; /** * Duplique la feuille courante en NOUVELLE FEUILLE INDÉPENDANTE (les données ne se * propageront pas — le pendant « suivra les données » est la création d'une VUE). * Le contenu affiché ne change pas : seule l'identité de feuille change. * @returns l'id de la feuille créée. */ duplicateCurrentSheetAsNewSheet(): string; /** * Bascule vers une autre feuille : snapshot de la courante, puis chargement du snapshot * de la cible (même mécanique que les vues : unDraw + remplacement de la drawing_area, * via fromJSON/reset). L'historique undo/redo repart de zéro (comme à tout chargement). */ switchToSheet(id: string, draw?: boolean): void; /** Renomme une feuille (nom vide ignoré). */ renameSheet(id: string, name: string): void; /** * Supprime une feuille (jamais la dernière). Si c'est la courante, bascule d'abord sur * sa voisine (précédente, sinon suivante). */ deleteSheet(id: string, draw?: boolean): void; /** * Sérialise une drawing area avec la couche de persistance de la classe (miroir de * `loadDrawingAreaFromJSON`, surchargé en OpenSankey+ pour la persistance OSP). */ dumpDrawingAreaToJSON(drawing_area: Class_DrawingArea): Type_JSON; /** * Ouvre le dialogue draggable de l'éditeur texte SankeyMATIC (format d'échange). * Appelé après tout import SankeyMATIC : le texte source reste ainsi sous les yeux * de l'utilisateur, éditable et réappliquable. Sans effet en mode publish/statique, * qui ne monte pas les dialogues d'édition. * * @memberof Class_ApplicationData */ openSankeymaticEditor(): void; /** * Function to that fetch json data from an url (the file has to be compressed with gzip) * * @param {string} url_data * @memberof Class_ApplicationData */ readUrlJSON(url_data: string): Promise; /** * Postprocessing drawing area after JSON affectation * @protected * @memberof Class_ApplicationData */ protected _afterFromJSON(): void; /** * Update current drawing area data from a json_object * * /!\ Add to waiting spinner queue * * @param {Type_JSON} json_object * @memberof Class_ApplicationData */ updateFromJSON(json_object: Type_JSON, kwargs?: Type_JSON): void; /** * Renvoie le JSON de mise en page à réappliquer pour une vue donnée, extrait * d'un `current_json` produit par `toJSON()`. * * ATTENTION : `_toJSON` encode les vues en DELTA (`__patch`, cf. #254), ce qui * RETIRE de l'entrée de vue les clés identiques au master — dont * `version`/`format_version`. Réappliquer telle quelle une entrée delta ferait * croire à `fromJSON` qu'il s'agit d'un fichier pré-0.9 et déclencherait le * convertisseur legacy (crash `convert_tags`). Depuis #1316 (delta descendu en * OS), on DÉCODE donc le delta sur une copie pour retrouver le snapshot complet * de la vue avant réapplication — comportement identique en OS et OSP. */ getViewLayoutJSON(view_id: string, current_json: Type_JSON): Type_JSON; /** * Persist the state of the current drawing area into a per-view compressed * cache. No-op for plain OS; ApplicationDataOSP overrides it to refresh * `_views[current_view_id].json` so a subsequent view switch or save reflects * the latest changes done on the current view's drawing area. */ saveCurrentViewToCache(): void; /** * Update current drawing area data from a json_object * @param {Type_JSON} json_object * @memberof Class_ApplicationData */ protected _updateFromJSON(json_object: Type_JSON, kwargs?: Type_JSON): void; draw(): void; /** * OS#388 — À appeler quand seule la LARGEUR RÉSERVÉE du fenêtrage change (barre latérale * ancrée ouverte/fermée/redimensionnée, colonne tableur/doc…). Contrairement à `draw()`, * ne reconstruit pas le SVG et ne passe PAS par le toast d'attente : un geste de fenêtrage * doit être instantané, le toast reste réservé aux opérations réellement lourdes. * @memberof Class_ApplicationData */ refreshWindowFraming(): void; /** * Applique l'état initial demandé par les options de publication (`publish_options`) : * présélection d'un data tag dans un ou plusieurs groupes, puis mode de navigation * (absolu / proportionnel / échelle adaptée). À appeler APRÈS le chargement du diagramme * (et l'éventuel layout), une fois que les tags et positions existent. * * - `data_tag_selection` est un dict { groupe : tag } où groupe/tag se résolvent par id OU par nom. * Appliqué AVANT le mode car les modes proportionnel/échelle capturent leur référence sur le * datatag courant. * - `view_tag_selection` est un dict { groupe : tag } (même résolution id/nom) qui sélectionne la * valeur ET active le filtre vue (view_mode) du groupe, comme l'œil dans la barre du bas. * - `position_mode` impose le mode de positionnement, comme un clic dans la barre du bas. * - sa#397 : `view` ouvre sur une vue (id OU nom) ; `view_label` restreint le sélecteur de vues * aux vues portant ce LABEL DE VUE (sa#396) et ouvre sur la première du groupe. Les deux sont * additives et tolérantes : valeur inconnue => option ignorée (warn), affichage inchangé. * @memberof Class_ApplicationData */ applyPublishStateOptions(): void; /** * sa#409 — Crochet du banc d'upgrade headless (`window.sankey.export_json = true`). * Expose sur `window` le fichier re-sérialisé au format COURANT (`__sankey_upgraded_json`, * chaîne JSON) et un résumé indépendant du delta (`__sankey_upgrade_meta`) que le pilote * (Playwright, cf. server/publish_upgrade.py) confronte au fichier SOURCE : version, comptes * nœuds/flux racine, et par vue son nom, ses labels de vues et son nombre de zones de texte * (clé `labels` du diagramme de la vue — cf. la collision corrigée d'sa#396 : c'est * précisément ce que l'upgrade ne doit jamais perdre). En cas d'échec, `__sankey_upgrade_error` * porte le message et rien d'autre n'est posé. */ protected _exportUpgradedJSON(): void; /** * Applique une sélection de tags `{ groupe : tag }` (groupe/tag résolus par id OU par nom), * partagée par les options de publication (`applyPublishStateOptions`) et par l'état * d'affichage transmis en paramètres d'URL (`applyUrlStateParams`). Ne redessine pas : * l'appelant enchaîne son propre `draw()`. * * os#1372 — `only_if_changed` : ne ré-applique pas une sélection DÉJÀ posée. Réservé aux * RÉ-applications (cf. `applyPublishStateOptions`) ; la toute première passe reste inchangée. * @memberof Class_ApplicationData */ applyTagSelections(data_tag_selection?: { [group: string]: string; } | null, view_tag_selection?: { [group: string]: string; } | null, only_if_changed?: boolean): void; /** * Sérialise l'état d'affichage COURANT (vue active + sélections de data tags / view tags) en * paramètres d'URL. Sert à rouvrir le diagramme ailleurs exactement tel qu'il est affiché ici * — bouton « Éditer » d'un site publié (cf. MenuTop), qui sans cela retombait sur la vue * maître. Symétrique de `applyUrlStateParams`. * @memberof Class_ApplicationData */ private static readonly URL_STATE_KEYS; /** Signature du dernier état écrit dans la barre d'adresse — évite un `replaceState` inutile. */ private _url_state_signature; /** * Tant que l'état initial de l'URL n'a pas été appliqué, on n'écrit pas : sinon le premier * dessin écraserait les paramètres qu'on s'apprête tout juste à lire. */ private _url_sync_enabled; /** * Reporte l'état de lecture courant dans la barre d'adresse, sans entrée d'historique * (`replaceState` : le bouton Retour reste celui de la navigation, pas des réglages). * * C'est ce qui rend l'état PARTAGEABLE : avant, `getUrlStateParams` n'avait qu'un appelant, * le bouton « Éditer » d'une page publiée — l'adresse ne bougeait jamais, et il n'y avait donc * rien à copier. * @memberof Class_ApplicationData */ /** * Arme la synchronisation sans rien appliquer. Pour les chargements qui ne passent pas * par `?url=` (reprise du cache, diagramme inline, page vierge) : il n'y a alors aucun * état à lire, mais l'adresse doit tout de même devenir vivante. * @memberof Class_ApplicationData */ enableUrlStateSync(): void; syncUrlState(): void; getUrlStateParams(): URLSearchParams; /** * Rejoue l'état d'affichage transmis en paramètres d'URL (`view`, `dt`, `vt`) — cf. * `getUrlStateParams`. À appeler APRÈS le chargement du diagramme (`readUrlJSON`), une fois * les vues et les tags présents. Sans effet si aucun de ces paramètres n'est présent. * @memberof Class_ApplicationData */ applyUrlStateParams(params: URLSearchParams): void; /** * Create a waiting toast and add function to waiting queue. * @param {() => void} funct * @param {Type_TextForToastPromise} [intake] Info text for loading, success or error * @memberof Class_ApplicationData */ sendWaitingToast(funct: () => void | Promise, // Accepte async intake?: Type_TextForToastPromise): void; pre_process_export_svg(convert_fo?: boolean): string; /** * (Re)construit le scénario de la visite guidée. Appelé à chaque lancement du tour (bouton Aide, * écran d'accueil) car le scénario dépend de l'état du diagramme au moment du lancement. * * `_steps` est muté EN PLACE : le TourProvider reçoit `app_data.steps` et garde la même * référence de tableau d'un lancement à l'autre. */ setSteps(): void; get guided_tour(): Class_GuidedTour; /** * Generatric function used to save undo/redo of some basic attribute mutation * (exemple : the color of the DA background), * it generate types of key, value and func according to model passed has parameter * * @template TModel * @template TKey * @param {TModel} model * @param {TKey} key * @param {TModel[TKey]} value * @param {(_:TModel[TKey])=>void} func * @memberof Class_ApplicationData */ setValueAndSaveHistory(model: TModel, key: TKey, value: TModel[TKey], func: (_: TModel[TKey]) => void): 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 {() => void} process_func * @memberof Class_MenuConfig */ _add_waiting_process(process_id: string, process_func: () => void, timer?: number): void; /** * Cancel a timed out process - It wont happen * @protected * @param {string} process_id * @memberof Class_MenuConfig */ protected _cancel_waiting_process(process_id: string): void; /** * Function to create custom application behavior when we press a key, * * Note : even if this is a class method we have to ref the curr class in parametter because 'this' take another scope when it is called in onkeydown * * @protected * @param {Class_ApplicationData} app_ref * @return {*} * @memberof Class_ApplicationData */ keyboardEventListener(app_ref: Class_ApplicationData): (evt: KeyboardEvent) => void; /** * Process all keyboard events on application * @param evt * @param app_ref */ protected _keyboardEventProcessing(evt: KeyboardEvent, app_ref: Class_ApplicationData): void; /** * Check if focus is on drawing area or not. * Avoid colisions between text inputs in menu & keyboard events on drawing area * @returns */ protected _isDrawingAreaActive(): boolean; /** * Allows to create a waiting toast for given function. * Use a functions queue to ensure that all function that call always run in the calling order. * * @protected * @param {() => void} funct * @param {string} funct_id * @param {Type_TextForToastPromise} [intake] * @memberof Class_ApplicationData */ protected _sendWaitingToast(funct: () => void | Promise, funct_id: string, intake?: Type_TextForToastPromise): void; /** * Some pre-process to correct html we will send to converter * because there is some difference between what our code produce * & what the converter wait to correctly produce an image * * @protected * @return {*} * @memberof Class_ApplicationData */ protected _pre_process_export_svg(): d3.Selection | undefined; get t(): TFunction<"translation", undefined>; set t(_: TFunction<"translation", undefined>); get i18n(): i18n; set i18n(_: i18n); get is_static(): boolean; get history(): Class_ApplicationHistory; /** Réinitialise l'historique undo/redo (appelé au switch de vue par ViewsReader). */ resetHistory(): void; viewsFromJSON(json_object: Type_JSON): void; setCurrentView(id: string): void; requestViewChange(id: string): void | Promise; setCurrentViewToMaster(): void | Promise; setCurrentViewToNext(): void | Promise; setCurrentViewToPrev(): void | Promise; navigateToView(id: string): void | Promise; extractViewFromJSON(json_object: Uint8Array, view_id: string): void; getDrawingAreaFromViewId(id: string): Class_DrawingArea | undefined; pushViewIdInViewOrder(id: string): void; moveViewUpInOrder(id: string): void; moveViewDownInOrder(id: string): void; applyViewTagSelection(selection: { [view_tagg_id: string]: string; } | undefined): void; get has_views(): boolean; get is_view_master(): boolean; get is_current_view_light(): boolean; get has_master_sankey(): boolean; get views_navigation_order(): string[]; get all_view_labels(): string[]; viewIdsWithLabel(label: string): string[]; get master_view(): Class_DrawingArea | undefined; get has_view_before(): boolean; get has_view_after(): boolean; get layout_view_sources(): Array<{ id: string; name: string; }>; get icon_library(): Class_IconLibrary; get steps(): StepType[]; get drawing_area(): Class_DrawingArea; protected set drawing_area(value: Class_DrawingArea); get menu_configuration(): Class_MenuConfig; protected set menu_configuration(value: Class_MenuConfig); get url_prefix(): string; get logo(): string; get logo_opensankey(): string; get logo_terriflux(): string; get logo_width(): number; set logo_width(value: number); get app_name(): string; set app_name(value: string); get transform_layout_all_attr(): string[]; /** * Group aliases for diagram_layout_options. * Override in subclasses to add module-specific groups. */ protected get _layout_groups(): Record; /** * Expands group aliases in a mode array into their constituent keys. * Unknown keys are passed through as-is (they may be valid leaf keys). */ expandLayoutMode(mode: string[]): string[]; get language(): string | undefined; set language(value: string | undefined); get file_name(): string; set file_name(value: string); get static_diagram_file(): string | null; set static_diagram_file(value: string | null); get sankeytheque_origin(): Type_SankeythequeOrigin | null; set sankeytheque_origin(value: Type_SankeythequeOrigin | null); get library_ref(): Type_LibraryRef | null; set library_ref(value: Type_LibraryRef | null); get documentation_markdown(): string; set documentation_markdown(value: string); get documentation_markdown_map(): Type_DocMarkdownMap; set documentation_markdown_map(value: Type_DocMarkdownMap); get documentation_images(): { [id: string]: string; }; set documentation_images(value: { [id: string]: string; }); get publish_settings(): Type_JSON; set publish_settings(value: Type_JSON); }