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 :
| Fonction | Rô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").
const value = PanelBuilder.resolveField(poi, "attributes.rating");
// Équivalent à : poi.attributes?.ratingParamètres :
| Paramètre | Type | Description |
|---|---|---|
poi | object | Objet POI source |
fieldPath | string | Chemin avec notation point |
Retour : valeur résolue ou undefined
3. PanelBuilder.createPlainSection(label, innerContent, extraClass)
Crée une section simple (<section>) sans accordion.
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ètre | Type | Description |
|---|---|---|
label | string | null | Titre de la section (optionnel) |
innerContent | Node | string | Contenu (Node DOM ou texte brut) |
extraClass | string | null | Classes CSS additionnelles |
Retour : HTMLElement — élément <section>
Structure générée :
<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).
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ètre | Type | Description |
|---|---|---|
label | string | Titre de l'accordion |
innerContent | Node | string | Contenu de l'accordion |
options | object | Options |
options.defaultOpen | boolean | Ouvert par défaut (défaut : false) |
Retour : HTMLElement — élément <section class="gl-accordion">
Structure générée :
<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>.
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ètre | Type | Description |
|---|---|---|
value | any | Valeur à afficher (convertie en string) |
variant | "multiline" | null | Style 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.
// 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ètre | Type | Description |
|---|---|---|
value | Array | string | Tableau 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 :
stringounumber— 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.
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ètre | Type | Description |
|---|---|---|
value | Array | Tableau d'objets (lignes) |
item.columns | Array | Définition des colonnes [{ key, label }] |
item.borders | object | Bordures { 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.outer | gl-poi-panel__table--border-outer |
borders.row | gl-poi-panel__table--border-row |
borders.column | gl-poi-panel__table--border-column |
borders.color | Attribut data-gl-border-color sur <table> |
8. PanelBuilder.renderGallery(value)
Rend une galerie d'images avec <figure> et <figcaption>.
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ètre | Type | Description |
|---|---|---|
value | Array | Tableau 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 :
<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).
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ètre | Type | Description |
|---|---|---|
value | Array | { recent: Array } | Tableau d'avis ou objet avec propriété recent |
Propriétés d'un avis :
| Propriété | Type | Description |
|---|---|---|
authorName | string | Nom de l'auteur |
rating | number | Note (affichée X.X/5) |
source | string | Source (ex. "Google", "TripAdvisor") |
title | string | Titre de l'avis |
text/comment | string | Corps de l'avis |
date/createdAt | string | Date |
helpfulCount | number | Nombre de "trouvé utile" |
url | string | Lien 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.
const content = PanelBuilder.buildLayoutItemContent(poiData, {
type: "list",
field: "attributes.openingHours",
});Paramètres :
| Paramètre | Type | Description |
|---|---|---|
poi | object | Objet POI source |
item | object | Configuration de l'item de layout |
item.type | string | Type de rendu (voir tableau ci-dessous) |
item.field | string | Chemin de propriété (notation point) |
item.variant | string | Variante (ex. "multiline" pour text) |
Types supportés :
type | Fonction appelée | Description |
|---|---|---|
"text" | renderText() | Texte simple ou multiline |
"list" | renderList() | Liste <ul> |
"table" | renderTable() | Tableau HTML |
"gallery" | renderGallery() | Galerie d'images |
"reviews" | renderReviews() | Bloc d'avis |
| Autre | fallback <div> | Texte brut |
Note : les types
title,subtitle,rating,image,link,phone,address,badge,tags,section,accordion,divider,htmlsont gérés par les couches supérieures (renderer POI) qui utilisentbuildLayoutItemContentcomme brique de base.
Retour : HTMLElement | null — null 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 :
[
{ "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
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
