Skip to content

AbstractRenderer — API de la classe de base des renderers

Product Version : GeoLeaf Platform V2

Version : 2.0.0

Fichier source : packages/core/src/modules/utils/renderers/abstract-renderer.ts

Dernière mise à jour : mars 2026


Vue d'ensemble

AbstractRenderer est la classe de base commune à tous les renderers POI de GeoLeaf. Elle centralise :

  • la résolution des dépendances (Log, Security, Config, Utils)
  • les helpers de création d'éléments DOM
  • l'enregistrement des event listeners avec cleanup automatique
  • la gestion d'état par élément via WeakMap
  • la gestion du cycle de vie (init / destroy)

Les renderers suivants étendent cette classe :

  • FieldRenderers — champs texte, badges, liens, tags
  • ComponentRenderers — listes, tableaux, évaluations
  • MediaRenderers — images, galeries, vidéos

Constructeur

ts
new AbstractRenderer(options?)
ParamètreTypeDéfautDescription
options.namestring"Renderer"Nom du renderer (utilisé dans les logs)
options.configobject{}Configuration personnalisée
options.debugbooleanfalseActive les logs debug
ts
class MyRenderer extends AbstractRenderer {
    constructor(options = {}) {
        super({
            name: "MyRenderer",
            debug: options.debug ?? false,
            config: { showIcon: options.showIcon ?? true },
        });
        this.init();
    }

    render(data: unknown, options?: unknown): HTMLElement {
        const el = this.createElement("div", "my-renderer");
        // ... rendering logic
        return el;
    }
}

Résolution des dépendances

getLog()

Retourne l'instance GeoLeaf.Log ou console en fallback.

ts
const log = this.getLog();
log.info("[MyRenderer] rendering", data);

getSecurity()

Retourne les utilitaires de sécurité (escapeHtml, setSafeHTML) avec fallbacks.

ts
const sec = this.getSecurity();
const safe = sec.escapeHtml(userInput);
sec.setSafeHTML(element, htmlContent);

getUtils()

Retourne GeoLeaf.Utils avec notamment resolveField.

ts
const utils = this.getUtils();
const title = utils.resolveField(poi, "title", "label", "name");

getActiveProfile()

Retourne le profil actif via GeoLeaf.Config.getActiveProfile().

ts
const profile = this.getActiveProfile();
if (profile?.icons) {
    // use profile icons
}

Logs

log(level, message, ...args)

ts
this.log("info", "Rendering POI", poi.title);
this.log("error", "Failed to load image", error);

Méthodes de commodité

ts
this.debug("Debug message"); // Only if debug=true
this.info("Info message");
this.warn("Warning message");
this.error("Error message");

Format de sortie : [RendererName] Message ...args


Construction DOM

createElement(tagName, className, attributes?)

Crée un élément DOM avec classe(s) et attributs.

ts
// Single class
const div = this.createElement("div", "my-class");

// Multiple classes
const btn = this.createElement("button", ["btn", "btn-primary"]);

// With attributes
const input = this.createElement("input", "field", {
    type: "text",
    placeholder: "Enter text",
    "data-id": "123",
});

createTextNode(text)

Crée un nœud texte sans injection HTML.

ts
const node = this.createTextNode("Safe content");
element.appendChild(node);

createTextElement(tagName, text, className?)

Crée un élément avec contenu texte sécurisé.

ts
const p = this.createTextElement("p", "Description du POI", "poi-desc");
// <p class="poi-desc">Description du POI</p>

createHTMLElement(tagName, html, className?)

Crée un élément avec HTML sanitisé via Security.setSafeHTML.

ts
const div = this.createHTMLElement("div", "<strong>Titre</strong>", "content");

appendChildren(parent, ...children)

Ajoute plusieurs enfants à un parent (chaînable).

ts
this.appendChildren(
    container,
    this.createTextElement("h2", "Titre"),
    this.createTextElement("p", "Description")
);

Gestion des événements

addEventListener(element, event, handler, options?)

Enregistre un listener avec cleanup automatique à la destruction.

ts
this.addEventListener(button, "click", (e) => {
    console.log("Clicked!", e);
});

// Avec options
this.addEventListener(container, "scroll", this.handleScroll, { passive: true });

// Retourne une fonction de cleanup manuel
const cleanup = this.addEventListener(el, "mouseover", handler);
cleanup(); // cleanup immédiat si nécessaire

removeAllEventListeners()

Retire tous les listeners enregistrés. Appelé automatiquement par destroy().


Gestion d'état (WeakMap)

L'état est stocké par élément via WeakMap — il est automatiquement libéré quand l'élément est supprimé du DOM.

setState(element, state)

ts
this.setState(container, {
    poi: { id: "123", title: "Restaurant" },
    renderTime: Date.now(),
});

getState(element)

ts
const state = this.getState(container);
console.log(state?.poi.title);

updateState(element, updates)

Fusionne les mises à jour avec l'état existant.

ts
this.updateState(container, { lastClicked: Date.now() });

deleteState(element)

ts
this.deleteState(container);

Cycle de vie

init()

Initialise le renderer. À surcharger pour une initialisation personnalisée.

ts
init() {
    super.init(); // toujours appeler le parent
    this.debug("Custom initialization");
}

isInitialized()

ts
if (!this.isInitialized()) {
    this.warn("Renderer not ready");
    return;
}

destroy()

Libère toutes les ressources. À surcharger si nécessaire.

ts
destroy() {
    this.debug("Custom cleanup");
    // custom cleanup here
    super.destroy(); // toujours appeler le parent
}

destroy() appelle automatiquement removeAllEventListeners() et vide le WeakMap d'état.


Méthode abstraite

render(data, options?)

Doit être implémentée par les sous-classes. Lance une erreur si non implémentée.

ts
render(data: unknown, options?: unknown): HTMLElement {
    const el = this.createElement("div", "my-renderer");
    // ... rendering logic
    return el;
}

Exemple complet

ts
class POICardRenderer extends AbstractRenderer {
    constructor(options = {}) {
        super({
            name: "POICardRenderer",
            debug: options.debug ?? false,
        });
        this.init();
    }

    render(poi: Record<string, unknown>): HTMLElement | null {
        if (!poi) {
            this.warn("No POI data provided");
            return null;
        }

        const utils = this.getUtils();
        const title = (utils.resolveField(poi, "title", "label", "name") as string) || "Sans titre";

        const card = this.createElement("div", "poi-card", { "data-poi-id": String(poi.id) });
        this.setState(card, { poi, renderTime: Date.now() });

        const heading = this.createTextElement("h3", title, "poi-card__title");
        card.appendChild(heading);

        this.addEventListener(card, "click", () => {
            card.dispatchEvent(
                new CustomEvent("poi:select", {
                    detail: { poi },
                    bubbles: true,
                })
            );
        });

        return card;
    }

    destroy() {
        this.debug("Destroying POICardRenderer");
        super.destroy();
    }
}

Tests unitaires

ts
describe("AbstractRenderer", () => {
    let renderer: AbstractRenderer;

    beforeEach(() => {
        renderer = new AbstractRenderer({ name: "TestRenderer", debug: true });
    });

    afterEach(() => {
        renderer.destroy();
    });

    test("createElement creates element with class", () => {
        const div = renderer.createElement("div", "test-class");
        expect(div.tagName).toBe("DIV");
        expect(div.className).toBe("test-class");
    });

    test("addEventListener cleans up on destroy", () => {
        const el = document.createElement("div");
        const handler = jest.fn();
        renderer.addEventListener(el, "click", handler);
        el.click();
        expect(handler).toHaveBeenCalledTimes(1);
        renderer.destroy();
        el.click();
        expect(handler).toHaveBeenCalledTimes(1); // not called again
    });

    test("state management round-trip", () => {
        const el = document.createElement("div");
        renderer.setState(el, { count: 0 });
        renderer.updateState(el, { count: 1 });
        expect(renderer.getState(el).count).toBe(1);
        renderer.deleteState(el);
        expect(renderer.getState(el)).toBeNull();
    });
});

Modules liés

  • packages/core/src/modules/built-in/poi/renderers/field-renderers.ts
  • packages/core/src/modules/built-in/poi/renderers/component-renderers.ts
  • packages/core/src/modules/built-in/poi/renderers/media-renderers.ts
  • packages/core/src/modules/built-in/poi/renderers/section-orchestrator.ts
  • packages/core/src/modules/utils/general/dom-security.ts

Version : 2.0.0

Licence : MIT

Dernière mise à jour : mars 2026

Released under the MIT License.