Skip to content

GeoLeaf.UI.PanelBuilder — Documentation

Product Version: GeoLeaf Platform V2 — Version doc: 2.0.0

Fichier source : packages/core/src/modules/built-in/ui/panel-builder.tsDernière mise à jour : mars 2026


Le module PanelBuilder construit dynamiquement les panneaux de détails POI en DOM pur (pas d'innerHTML). Il génère les éléments HTML des panneaux latéraux selon des layouts configurables définis dans les profils JSON.

PanelBuilder a été extrait de geoleaf.ui.ts lors d'une phase de refactorisation pour découpler la logique de construction de panneaux UI. Il opère exclusivement sur des objets DOM (HTMLElement), sans dépendance à un moteur cartographique.


1. API publique

L'objet PanelBuilder est exporté depuis panel-builder.ts et expose les fonctions suivantes :

FonctionRôle
resolveField(poi, fieldPath)Résout un chemin de propriété avec notation point
createPlainSection(label, content, cssClass)Crée une section simple (sans accordion)
createAccordionSection(label, content, opts)Crée une section accordion repliable
renderText(value, variant)Rend un champ texte simple ou multiline
renderList(value)Rend une liste <ul> depuis un tableau ou un texte
renderTable(value, item)Rend un tableau HTML depuis un tableau d'objets
renderGallery(value)Rend une galerie d'images <figure>
renderReviews(value)Rend un bloc d'avis/reviews
buildLayoutItemContent(poi, item)Dispatcher principal — construit un item de layout

2. PanelBuilder.resolveField(poi, fieldPath)

Résout un chemin de propriété avec notation point (ex : "attributes.shortDescription").

js
const value = PanelBuilder.resolveField(poi, "attributes.rating");
// Équivalent à : poi.attributes?.rating

Paramètres :

ParamètreTypeDescription
poiobjectObjet POI source
fieldPathstringChemin avec notation point

Retour : valeur résolue ou undefined


3. PanelBuilder.createPlainSection(label, innerContent, extraClass)

Crée une section simple (<section>) sans accordion.

js
const content = PanelBuilder.renderText("Texte de description", null);
const section = PanelBuilder.createPlainSection("Description", content, "gl-section--highlight");
document.getElementById("sidepanel").appendChild(section);

Paramètres :

ParamètreTypeDescription
labelstring | nullTitre de la section (optionnel)
innerContentNode | stringContenu (Node DOM ou texte brut)
extraClassstring | nullClasses CSS additionnelles

Retour : HTMLElement — élément <section>

Structure générée :

html
<section class="gl-poi-panel__section [extraClass]">
    <h3 class="gl-poi-panel__section-title">Label</h3>
    <div class="gl-poi-panel__section-body">…</div>
</section>

4. PanelBuilder.createAccordionSection(label, innerContent, options)

Crée une section accordion repliable (<section> avec <button> toggle).

js
const list = PanelBuilder.renderList(["Lundi 9h-18h", "Mardi 9h-18h"]);
const accordion = PanelBuilder.createAccordionSection("Horaires", list, { defaultOpen: true });
document.getElementById("sidepanel").appendChild(accordion);

Paramètres :

ParamètreTypeDescription
labelstringTitre de l'accordion
innerContentNode | stringContenu de l'accordion
optionsobjectOptions
options.defaultOpenbooleanOuvert par défaut (défaut : false)

Retour : HTMLElement — élément <section class="gl-accordion">

Structure générée :

html
<section class="gl-accordion gl-poi-panel__section [gl-is-open]">
    <button type="button" class="gl-accordion__header">
        <span class="gl-accordion__title">Label</span>
        <span class="gl-accordion__icon" aria-hidden="true">▾</span>
    </button>
    <div class="gl-accordion__body">…</div>
</section>

5. PanelBuilder.renderText(value, variant)

Rend un champ texte dans un <div>.

js
const el = PanelBuilder.renderText("Bienvenue au musée du Louvre", null);
// Ou multiline :
const el = PanelBuilder.renderText("Ligne 1\nLigne 2", "multiline");

Paramètres :

ParamètreTypeDescription
valueanyValeur à afficher (convertie en string)
variant"multiline" | nullStyle de rendu

Retour : HTMLElement<div class="gl-poi-panel__text [--multiline]">


6. PanelBuilder.renderList(value)

Rend une liste <ul> depuis un tableau ou un texte multiligne.

js
// Depuis un tableau
const el = PanelBuilder.renderList(["Musée", "Patrimoine", "Culture"]);

// Depuis un tableau d'objets { label, value }
const el = PanelBuilder.renderList([
    { label: "Entrée", value: "12€" },
    { label: "Réduit", value: "8€" },
]);

// Depuis un texte multiligne
const el = PanelBuilder.renderList("Lundi : 9h-18h\nMardi : 9h-18h");

Paramètres :

ParamètreTypeDescription
valueArray | stringTableau d'items ou texte avec retours à la ligne

Retour : HTMLElement | null<ul class="gl-poi-panel__list"> ou null si vide

Formats d'entrée supportés pour les items du tableau :

  • string ou number — affiché directement
  • { label: string } — affiche le label
  • { value: string } — affiche la valeur
  • { label: string, value: string } — affiche "label : value"
  • { text: string } — affiche le texte

7. PanelBuilder.renderTable(value, item)

Rend un tableau HTML à partir d'un tableau d'objets et d'une configuration de colonnes.

js
const el = PanelBuilder.renderTable(
    [
        { nom: "Pain au chocolat", prix: "1.20€" },
        { nom: "Croissant", prix: "1.00€" },
    ],
    {
        columns: [
            { key: "nom", label: "Produit" },
            { key: "prix", label: "Prix" },
        ],
        borders: { outer: true, row: true, column: false, color: "#ccc" },
    }
);

Paramètres :

ParamètreTypeDescription
valueArrayTableau d'objets (lignes)
item.columnsArrayDéfinition des colonnes [{ key, label }]
item.bordersobjectBordures { outer, row, column, color }

Retour : HTMLElement | null — wrapper <div> contenant <table>, ou null si données vides ou colonnes absentes.

Classes CSS des bordures :

PropriétéClasse CSS ajoutée
borders.outergl-poi-panel__table--border-outer
borders.rowgl-poi-panel__table--border-row
borders.columngl-poi-panel__table--border-column
borders.colorAttribut data-gl-border-color sur <table>

8. PanelBuilder.renderGallery(value)

Rend une galerie d'images avec <figure> et <figcaption>.

js
const el = PanelBuilder.renderGallery([
    { url: "img/photo1.jpg", alt: "Vue extérieure", caption: "Façade" },
    { url: "img/photo2.jpg", alt: "Salle principale" },
    "img/photo3.jpg", // format simplifié (URL seule)
]);

Paramètres :

ParamètreTypeDescription
valueArrayTableau d'images { url, alt?, caption? } ou strings (URL simples)

Retour : HTMLElement | null<div class="gl-poi-panel__gallery"> ou null

Structure générée :

html
<div class="gl-poi-panel__gallery">
    <figure class="gl-poi-panel__gallery-item">
        <img src="img/photo1.jpg" alt="Vue extérieure" />
        <figcaption>Façade</figcaption>
    </figure>

</div>

9. PanelBuilder.renderReviews(value)

Rend un bloc d'avis/reviews (type avis voyageurs).

js
const el = PanelBuilder.renderReviews([
    {
        authorName: "Marie D.",
        rating: 4.5,
        source: "Google",
        title: "Excellent musée",
        text: "Une visite incontournable, collections magnifiques.",
        date: "2026-01-15",
        helpfulCount: 12,
        url: "https://example.com/review/123"
    }
]);

// Ou depuis un objet avec propriété 'recent' :
const el = PanelBuilder.renderReviews({ recent: [...] });

Paramètres :

ParamètreTypeDescription
valueArray | { recent: Array }Tableau d'avis ou objet avec propriété recent

Propriétés d'un avis :

PropriétéTypeDescription
authorNamestringNom de l'auteur
ratingnumberNote (affichée X.X/5)
sourcestringSource (ex. "Google", "TripAdvisor")
titlestringTitre de l'avis
text/commentstringCorps de l'avis
date/createdAtstringDate
helpfulCountnumberNombre de "trouvé utile"
urlstringLien vers l'avis original

Retour : HTMLElement | null


10. PanelBuilder.buildLayoutItemContent(poi, item)

Dispatcher principal. Construit le contenu d'un item de layout en déléguant aux fonctions de rendu spécialisées.

js
const content = PanelBuilder.buildLayoutItemContent(poiData, {
    type: "list",
    field: "attributes.openingHours",
});

Paramètres :

ParamètreTypeDescription
poiobjectObjet POI source
itemobjectConfiguration de l'item de layout
item.typestringType de rendu (voir tableau ci-dessous)
item.fieldstringChemin de propriété (notation point)
item.variantstringVariante (ex. "multiline" pour text)

Types supportés :

typeFonction appeléeDescription
"text"renderText()Texte simple ou multiline
"list"renderList()Liste <ul>
"table"renderTable()Tableau HTML
"gallery"renderGallery()Galerie d'images
"reviews"renderReviews()Bloc d'avis
Autrefallback <div>Texte brut

Note : les types title, subtitle, rating, image, link, phone, email, address, badge, tags, section, accordion, divider, html sont gérés par les couches supérieures (renderer POI) qui utilisent buildLayoutItemContent comme brique de base.

Retour : HTMLElement | nullnull si la valeur du champ est vide ou absente.


11. Format de layout complet (profil JSON)

Les layouts sont définis dans le profil geoleaf.config.json par type de POI :

json
[
    { "field": "attributes.mainImage", "type": "image", "fullWidth": true },
    { "field": "label", "type": "title" },
    { "field": "attributes.rating", "type": "rating", "maxStars": 5 },
    {
        "type": "section",
        "title": "Contact",
        "fields": [
            { "field": "attributes.phone", "type": "phone", "icon": "phone" },
            { "field": "attributes.email", "type": "email", "icon": "envelope" }
        ]
    },
    {
        "type": "accordion",
        "title": "Horaires",
        "defaultOpen": false,
        "fields": [{ "field": "attributes.openingHours", "type": "list" }]
    },
    { "field": "attributes.description", "type": "text", "variant": "multiline" },
    { "field": "attributes.gallery", "type": "gallery" },
    { "field": "attributes.reviews", "type": "reviews" }
]

12. Exemple d'utilisation complète

js
import { PanelBuilder } from "@geoleaf/core";

const poi = {
    label: "Musée du Louvre",
    attributes: {
        rating: 4.8,
        description: "Le plus grand musée d'art du monde.",
        openingHours: ["Lundi : Fermé", "Mardi-Dimanche : 9h-18h"],
        gallery: [
            { url: "img/louvre1.jpg", alt: "Pyramide", caption: "Entrée principale" },
            { url: "img/louvre2.jpg", alt: "Mona Lisa" },
        ],
    },
};

// Construire les sections du panneau
const sections = [
    PanelBuilder.createPlainSection(
        "Description",
        PanelBuilder.buildLayoutItemContent(poi, {
            type: "text",
            field: "attributes.description",
            variant: "multiline",
        }),
        null
    ),
    PanelBuilder.createAccordionSection(
        "Horaires",
        PanelBuilder.buildLayoutItemContent(poi, {
            type: "list",
            field: "attributes.openingHours",
        }),
        { defaultOpen: true }
    ),
    PanelBuilder.createPlainSection(
        "Photos",
        PanelBuilder.buildLayoutItemContent(poi, { type: "gallery", field: "attributes.gallery" }),
        null
    ),
];

const panel = document.getElementById("sidepanel");
sections.forEach((s) => s && panel.appendChild(s));

13. Références

  • Code source : packages/core/src/modules/built-in/ui/panel-builder.ts
  • Utilitaires DOM : packages/core/src/modules/utils/general/dom-helpers.ts
  • Résolution de champs : packages/core/src/modules/utils/general/general-utils.ts
  • Module POI : docs/poi/GeoLeaf_POI_README.md

Released under the MIT License.