Skip to content

Content Builder — Documentation Complète

Version: 2.0.0

Date: mars 2026

Module: GeoLeaf._ContentBuilder


Table des Matières

  1. Vue d'ensemble
  2. Architecture
  3. Modules
  4. API Reference
  5. Exemples d'utilisation
  6. Best Practices
  7. Troubleshooting

Vue d'ensemble

Le Content Builder est le système de génération de contenu HTML pour les POIs (Points d'Intérêt) dans GeoLeaf. Il gère la création de :

  • Popups : Fenêtres contextuelles sur la carte
  • Tooltips : Info-bulles au survol
  • Panels : Panneaux latéraux détaillés

Architecture Modulaire

Le Content Builder utilise une architecture modulaire en TypeScript :

built-in/ui/content-builder/
├── core.ts                        // Helpers, validators, badge resolver, formatters
├── templates.ts                   // Orchestrateur — re-exporte tous les template builders
├── templates-css-classes.ts       // Constantes CSS BEM
├── templates-primitives.ts        // Primitives HTML (wrapInDiv, buildLabel...)
├── templates-text-metric.ts       // Templates texte/métrique/rating/badge
├── templates-media-collection.ts  // Templates image/list/table/gallery/coordinates
├── assemblers.ts                  // Assembleurs popup/tooltip/panel
├── helpers.ts                     // Helpers utilitaires partagés
├── renderers-collection.ts        // Renderers : list, table, tags, gallery, reviews
├── renderers-geo.ts               // Renderers : coordonnées, géométrie
├── renderers-numeric.ts           // Renderers : number, metric, rating
├── renderers-shared.ts            // Logique renderer partagée
├── renderers-text.ts              // Renderers : text, longtext, link, badge, image
└── renderers-visual.ts            // Renderers visuels

Le module principal (content-builder.ts) importe et orchestre l'ensemble.

Avantages :

  • Modularité : Chaque module a une responsabilité claire
  • Testabilité : Modules isolés testables individuellement
  • Maintenabilité : Fichiers <400 lignes, code organisé
  • Réutilisabilité : Templates et helpers réutilisables
  • Sécurité : escapeHtml et validateUrl importés depuis built-in/security/index.ts

Architecture

Diagramme de Flux

USER INTERACTION
(Click POI, Hover, Open Panel)


POI Handler (built-in/poi/*)
Calls: buildPopupHTML / buildTooltipHTML / Panel


CONTENT BUILDER ORCHESTRATOR
(content-builder.ts)

├── buildPopupHTML(poi, config, options)
├── buildTooltipHTML(poi, config, options)
└── buildPanelItems(poi, config, options)


Assemblers Module (assemblers.ts)
- buildPopupHTML
- buildTooltipHTML
- buildPanelItems


Renderers (13+ types)
- renderText, renderBadge, renderImage
- renderList, renderTable, renderTags
- renderCoordinates, renderGallery, etc.


Core + Templates Modules
- Helpers, validators, badge resolver
- Template builders HTML


HTML CONTENT GENERATED
(Popup, Tooltip, Panel items)

Modules

1. core.ts

Responsabilité : Fonctions utilitaires, validateurs, résolution badges, formatage.

Exports :

javascript
GeoLeaf._ContentBuilder.Core = {
    // Helpers
    getResolveField(),       // POI field resolution with multi-path support
    getEscapeHtml(),         // Secure HTML escaping (from built-in/security)
    getActiveProfile(),      // Active profile config
    getLog(),                // System logger

    // Validators
    validateImageUrl(poi, field, options),
    validateCoordinates(poi, field),
    validateNumber(value, options),
    validateRating(value, options),

    // Badge Resolver
    resolveBadge(poi, config, options),
    resolveBadgeTooltip(badge),

    // Formatters
    formatNumber(value, options),
    formatCoordinates(lat, lng, format),
    formatRating(value, max),
}

Exemple :

javascript
const Core = GeoLeaf._ContentBuilder.Core;

// Image URL validation
const imageUrl = Core.validateImageUrl(poi, "attributes.photo", {
    defaultImage: "/images/default.jpg",
});

// Badge resolution
const badge = Core.resolveBadge(
    poi,
    { field: "attributes.categoryId" },
    { context: "popup", resolveCategoryDisplay: true }
);
// Returns: { icon, color, label, tooltip }

// Coordinate formatting
const formatted = Core.formatCoordinates(45.7578, 4.832);
// Returns: "45°45'28.1\"N, 4°49'55.2\"E"

2. templates.ts

Responsabilité : Orchestrateur des template builders HTML — importe et re-exporte les sous-modules.

Architecture des sous-modules :

  • templates-css-classes.ts — constantes CSS BEM (40+ classes)
  • templates-primitives.tsbuildLabel, wrapInParagraph, wrapInDiv
  • templates-text-metric.tscreateTextElement, createMetricElement, createBadge, createRatingElement, createLinkElement
  • templates-media-collection.tscreateImageElement, createListElement, createTableElement, createGalleryElement, createCoordinatesElement

Exports :

javascript
GeoLeaf._ContentBuilder.Templates = {
    CSS_CLASSES: { /* 40+ BEM class constants */ },

    // Primitives
    buildLabel(label, options),
    wrapInParagraph(content, className),
    wrapInDiv(content, className),

    // Text / Metric
    createTextElement(value, options),
    createLongtextElement(value, options),
    createNumberElement(value, options),
    createMetricElement(value, options),
    createBadge(badge, options),
    createStar(filled),
    createRatingElement(value, options),
    createLinkElement(href, label, options),

    // Media / Collection
    createImageElement(src, options),
    createListElement(items, options),
    createTableElement(data, options),
    createTag(label, options),
    createTagsElement(tags, options),
    createCoordinatesElement(lat, lng, options),
    createGalleryElement(images, options),
}

Exemple :

javascript
const Templates = GeoLeaf._ContentBuilder.Templates;

// Create metric
const metricHtml = Templates.createMetricElement(42.5, {
    suffix: "/m²",
    icon: "fa-euro",
});
// Returns: <div class="gl-metric">42.5 /m²</div>

// Create rating
const ratingHtml = Templates.createRatingElement(4.5, { max: 5 });
// Returns: <div class="gl-rating">4.5/5</div>

// Create badge
const badgeHtml = Templates.createBadge({
    label: "Restaurant",
    icon: "fa-utensils",
    color: "#e74c3c",
    tooltip: "Catégorie: Restaurants",
});

3. assemblers.ts

Responsabilité : Assembler les renderers en structures complètes (popup, tooltip, panel).

Exports :

javascript
GeoLeaf._ContentBuilder.Assemblers = {
    buildPopupHTML(poi, config, options),   // Full popup HTML
    buildTooltipHTML(poi, config, options), // Full tooltip HTML
    buildPanelItems(poi, config, options),  // Side panel items array
}

Exemple :

javascript
const Assemblers = GeoLeaf._ContentBuilder.Assemblers;

// Build Popup
const popupHtml = Assemblers.buildPopupHTML(
    poi,
    [
        { type: "image", field: "photo", variant: "hero", order: 1 },
        { type: "badge", field: "categoryId", order: 2 },
        { type: "text", field: "name", variant: "title", order: 3 },
        { type: "longtext", field: "description", order: 4 },
    ],
    { context: "popup" }
);

// Build Tooltip
const tooltipHtml = Assemblers.buildTooltipHTML(poi, [
    { type: "text", field: "name", order: 1 },
    { type: "badge", field: "categoryId", order: 2, contentUnion: " - " },
]);
// Returns: "Restaurant La Bonne Table - Restaurant"

// Build Panel Items
const panelItems = Assemblers.buildPanelItems(poi, [
    { type: "image", field: "photo", order: 1 },
    { type: "list", field: "services", label: "Services", accordion: true },
    { type: "table", field: "schedule", label: "Horaires" },
]);
// Returns: [{ html, config, label, accordion, defaultOpen }, ...]

4. content-builder.ts (orchestrateur)

Responsabilité : 13 renderers individuels + orchestration centrale.

Types de renderers :

TypeDescriptionExemple champ
textTexte simple (variants: title, short, long)name, title
longtextTexte multilignedescription, notes
numberValeur numérique formatéeprice, area, quantity
metricNombre avec préfixe/suffixe€ 89.50 /nuit
ratingNote avec étoiles4.5/5 ★
badgeBadge taxonomieCatégorie, Sous-catégorie
imageImage (variants: default, hero)photo, thumbnail
linkLien hypertextewebsite, booking_url
listListe HTMLservices[], amenities[]
tableTableau donnéesschedule, pricing
tagsCloud de tagskeywords[], tags[]
coordinatesGPS (DMS format)latitude, longitude
galleryGalerie imagesphotos[], gallery[]

Exports :

javascript
GeoLeaf._ContentBuilder = {
    // Individual renderers (13)
    renderText(poi, config, options),
    renderLongtext(poi, config, options),
    renderNumber(poi, config, options),
    renderMetric(poi, config, options),
    renderRating(poi, config, options),
    renderBadge(poi, config, options),
    renderImage(poi, config, options),
    renderLink(poi, config, options),
    renderList(poi, config, options),
    renderTable(poi, config, options),
    renderTags(poi, config, options),
    renderCoordinates(poi, config, options),
    renderGallery(poi, config, options),
    renderItem(poi, config, options),  // Dispatcher by type

    // Assemblers (delegated to assemblers.ts)
    buildPopupHTML(poi, config, options),
    buildTooltipHTML(poi, config, options),
    buildPanelItems(poi, config, options),

    // Utilities
    getResolveField(),
    getEscapeHtml(),
    getActiveProfile(),
}

API Reference

renderItem(poi, config, options)

Dispatcher principal. Appelle le renderer correspondant selon config.type.

Signature :

javascript
const html = GeoLeaf._ContentBuilder.renderItem(poi, config, options);

Paramètres :

  • poi (Object) : Données du POI
  • config (Object) :
    • type (string) : Type de renderer ("text", "image", "badge", etc.)
    • field (string) : Chemin du champ (dot notation)
    • label (string, optionnel) : Libellé à afficher
    • order (number, optionnel) : Ordre d'affichage
    • variant (string, optionnel) : Variante du renderer
  • options (Object, optionnel) :
    • context : "popup" | "tooltip" | "panel" (défaut: "popup")

Retourne : string HTML

Exemple :

javascript
const html = GeoLeaf._ContentBuilder.renderItem(
    poi,
    { type: "text", field: "attributes.name", variant: "title" },
    { context: "popup" }
);

Core.getResolveField()

Retourne une fonction de résolution de champs POI avec support multi-paths.

javascript
const resolveField = GeoLeaf._ContentBuilder.Core.getResolveField();

// Test in order: poi.attributes.name → poi.attributes.nom → poi.properties.name
const name = resolveField(poi, "attributes.name", "attributes.nom", "properties.name");

Core.validateImageUrl(poi, field, options)

Valide et normalise une URL d'image. Utilise validateUrl() du module built-in/security.

Paramètres :

  • poi (Object) : POI source
  • field (string) : Champ à valider
  • options (Object) :
    • defaultImage (string) : Image par défaut si invalide
    • allowedPatterns (Array<RegExp>) : Patterns autorisés (défaut: https://, http://, data:image/)

Retourne : URL validée ou defaultImage


Exemples d'utilisation

Exemple 1 : Popup standard

javascript
const layout = [
    { type: "image", field: "photo", variant: "hero", order: 1 },
    { type: "badge", field: "categoryId", order: 2 },
    { type: "text", field: "name", variant: "title", order: 3 },
    { type: "metric", field: "price", label: "Prix", suffix: "€/nuit", order: 4 },
    { type: "rating", field: "rating", order: 5 },
    { type: "link", field: "website", label: "Réserver", order: 6 },
];

const popupHtml = GeoLeaf._ContentBuilder.buildPopupHTML(poi, layout, { context: "popup" });

Exemple 2 : Tooltip minimal

javascript
const tooltipLayout = [
    { type: "text", field: "name", order: 1 },
    { type: "badge", field: "categoryId", order: 2, contentUnion: " — " },
];

const tooltipHtml = GeoLeaf._ContentBuilder.buildTooltipHTML(poi, tooltipLayout);

Exemple 3 : Panneau détail avec accordéons

javascript
const panelLayout = [
    { type: "image", field: "photo", variant: "hero", order: 1 },
    { type: "badge", field: "categoryId", order: 2 },
    { type: "text", field: "name", variant: "title", order: 3 },
    { type: "longtext", field: "description", label: "Description", accordion: true, order: 4 },
    { type: "list", field: "amenities", label: "Équipements", accordion: true, order: 5 },
    { type: "table", field: "schedule", label: "Horaires", order: 6 },
    { type: "coordinates", field: "coordinates", label: "Position GPS", order: 7 },
];

const panelItems = GeoLeaf._ContentBuilder.buildPanelItems(poi, panelLayout);
// panelItems: [{ html, config, label, accordion, defaultOpen }, ...]

Exemple 4 : Renderer individuel

javascript
// Render a gallery directly
const galleryHtml = GeoLeaf._ContentBuilder.renderGallery(
    poi,
    { field: "attributes.photos", maxItems: 6 },
    { context: "panel" }
);

// Render coordinates
const coordHtml = GeoLeaf._ContentBuilder.renderCoordinates(poi, {
    field: "geometry.coordinates",
    format: "dms",
});

Best Practices

Sécurité

javascript
// All text fields are automatically escaped via escapeHtml() from built-in/security
// Do not bypass this — never pass unsanitized poi.properties directly to innerHTML

// Good: use renderItem
const html = GeoLeaf._ContentBuilder.renderItem(poi, { type: "text", field: "name" });

// Bad: manual innerHTML with raw data
container.innerHTML = poi.attributes.name; // XSS risk

Performance

javascript
// Build all content at once with buildPopupHTML (single pass)
const html = GeoLeaf._ContentBuilder.buildPopupHTML(poi, layout);

// Avoid calling renderItem in a loop for large panels
// Prefer buildPanelItems which batches rendering

Ordre des items

javascript
// Items are sorted by "order" property before rendering
const layout = [
    { type: "text", field: "name", order: 1 },
    { type: "rating", field: "rating", order: 3 },
    { type: "badge", field: "categoryId", order: 2 }, // will be rendered second
];

Troubleshooting

Champ vide dans le rendu

javascript
// Verify field path with resolveField
const resolveField = GeoLeaf._ContentBuilder.Core.getResolveField();
const value = resolveField(poi, "attributes.rating");
console.log("Field value:", value);

// Enable debug logging
GeoLeaf.Config.setDebug({ enabled: true, modules: ["content-builder"] });

Image non affichée

javascript
// validateImageUrl returns null if URL is invalid
const imageUrl = GeoLeaf._ContentBuilder.Core.validateImageUrl(poi, "attributes.photo", {
    defaultImage: "/images/placeholder.jpg",
});
console.log("Validated image URL:", imageUrl);

Badge sans texte

javascript
// Verify taxonomy is loaded
const profile = GeoLeaf.Config.getActiveProfile();
console.log("Taxonomy:", profile?.taxonomy?.categories);

// The badge resolver needs taxonomy to resolve categoryId → label
const badge = GeoLeaf._ContentBuilder.Core.resolveBadge(
    poi,
    { field: "attributes.categoryId" },
    { context: "popup", resolveCategoryDisplay: true }
);
console.log("Badge resolved:", badge);

Version : 2.0.0

Dernière mise à jour : mars 2026

Released under the MIT License.