GeoLeaf UI Components — Documentation Détaillée
Product Version: GeoLeaf Platform V2
Modules : GeoLeaf._UIComponents, GeoLeaf._UIDomUtils, GeoLeaf.UI.Notifications, et composants UI
Version : 2.0.0
Fichiers source (monorepo) : packages/core/src/modules/built-in/ui/
Dernière mise à jour : mars 2026
Vue d'ensemble
Le système de composants UI GeoLeaf fournit des briques réutilisables pour construire l'interface utilisateur de la cartographie. Ces composants standardisent :
- La création d'éléments DOM avec cleanup automatique
- Les patterns d'accordéons et panneaux
- Les notifications toast
- Les symboles de légende (cercles, lignes, polygones)
- Les contrôles MapLibre (échelle, coordonnées)
- La gestion d'événements et états UI
Architecture des composants UI
built-in/ui/
├── ui-api.ts // Orchestrateur principal
├── theme.ts // Gestion thèmes (light/dark)
├── controls.ts // Contrôles MapLibre (fullscreen, géoloc, POI add, theme toggle)
├── notifications.ts // Système de toast notifications
├── event-delegation.ts // Délégation d'événements
├── filter-panel.ts // Panneau de filtres (entry point lazy)
├── filter-panel/ // Système de filtrage (6 fichiers)
│ ├── filter-panel-accordion.ts // Accordéons du panneau filtres
│ ├── lazy-loader.ts // Chargement lazy des composants
│ ├── proximity-manual-mode.ts // Mode manuel géolocalisation
│ ├── proximity-state.ts // État géolocalisation
│ ├── shared.ts // Utilitaires partagés
│ └── svg-helpers.ts // Helpers SVG
├── filter-state-manager.ts // Gestion états filtres
├── geolocation-state.ts // État géolocalisation
├── mobile-toolbar-state.ts // État toolbar mobile
├── content-builder.ts // Entry point content builder
└── content-builder/ // Modules du content builder (voir README dédié)Module 1 : GeoLeaf._UIComponents (composants réutilisables)
Rôle
Fournit des composants réutilisables pour Legend et LayerManager : accordéons, symboles de légende, éléments de style.
API Principale
createAccordion(container, config)
Crée un accordéon avec header cliquable et body collapsible.
Paramètres :
container(HTMLElement) : Conteneur parentconfig(Object) :layerId: ID de la couchelabel: Titre de l'accordéoncollapsed: État initial (replié ou non)visible: Couche visible (grise si false)onToggle: Callback lors du toggle
Retourne : { accordionEl, headerEl, bodyEl, toggleEl }
Exemple :
const legendContainer = document.querySelector(".gl-legend__body");
const { accordionEl, bodyEl } = GeoLeaf._UIComponents.createAccordion(legendContainer, {
layerId: "parcs",
label: "Parcs et Jardins",
collapsed: false,
visible: true,
onToggle: (layerId, isExpanded) => {
console.log(`Accordion ${layerId} is now ${isExpanded ? "open" : "closed"}`);
},
});
bodyEl.appendChild(document.createTextNode("Legend content"));renderCircleSymbol(container, config)
Rend un symbole circulaire (pour POI/markers) avec icône SVG optionnelle.
Paramètres :
container: Conteneur du symboleconfig:radius: Rayon en pixels (défaut: 8)fillColor: Couleur de remplissagecolor: Couleur de bordureweight: Épaisseur bordurefillOpacity: Opacitéicon: ID d'icône SVG sprite (ex:'#tree')iconColor: Couleur de l'icône
Exemple :
const symbolContainer = document.createElement("div");
// Simple circle
GeoLeaf._UIComponents.renderCircleSymbol(symbolContainer, {
radius: 10,
fillColor: "#228B22",
color: "#006400",
weight: 2,
fillOpacity: 0.8,
});
// Circle with icon
GeoLeaf._UIComponents.renderCircleSymbol(symbolContainer, {
radius: 12,
fillColor: "#FF5733",
icon: "#restaurant",
iconColor: "#FFFFFF",
});renderLineSymbol(container, config)
Rend un symbole de ligne (pour routes/LineString).
GeoLeaf._UIComponents.renderLineSymbol(symbolContainer, {
color: "#3388ff",
weight: 3,
opacity: 1,
dashArray: "5, 10",
});renderPolygonSymbol(container, config)
Rend un symbole de polygone (pour zones/Polygon).
GeoLeaf._UIComponents.renderPolygonSymbol(symbolContainer, {
fillColor: "#3388ff",
fillOpacity: 0.4,
color: "#0066cc",
weight: 2,
});clearElement(element)
Vide un élément DOM de manière sécurisée (sans innerHTML).
const container = document.getElementById("legend-body");
GeoLeaf._UIComponents.clearElement(container);createEmptyMessage(container, message, className)
Crée un message d'état vide (aucune donnée, chargement, etc.).
GeoLeaf._UIComponents.createEmptyMessage(
container,
"Aucune couche à afficher.",
"gl-legend__empty"
);attachEventHandler(element, eventType, handler)
Attache un event listener avec cleanup automatique.
const cleanup = GeoLeaf._UIComponents.attachEventHandler(button, "click", () =>
console.log("Clicked!")
);
// Later: remove the listener
cleanup();Module 2 : GeoLeaf._UIDomUtils (dom-utils)
Rôle
Utilitaires DOM pour résolution de champs, taxonomie, et manipulation UI.
API Principale
resolveField(obj, fieldPath)
Résout une valeur via un chemin de propriété (dot notation).
Paramètres :
obj: Objet source (POI, route, etc.)fieldPath: Chemin séparé par des points (ex:'attributes.reviews.rating')
Retourne : Valeur trouvée ou undefined
Exemple :
const poi = {
id: "poi_123",
title: "Restaurant",
attributes: {
address: "10 rue Paris",
reviews: {
rating: 4.5,
count: 127,
},
},
};
const rating = GeoLeaf._UIDomUtils.resolveField(poi, "attributes.reviews.rating");
// → 4.5
const address = GeoLeaf._UIDomUtils.resolveField(poi, "attributes.address");
// → '10 rue Paris'attachAccordionBehavior(container)
Attache le comportement d'accordéon à un conteneur (délégation d'événements).
const panelContainer = document.querySelector(".gl-panel");
GeoLeaf._UIDomUtils.attachAccordionBehavior(panelContainer);getActiveProfileConfig()
Récupère le profil actif depuis GeoLeaf.Config.
const profile = GeoLeaf._UIDomUtils.getActiveProfileConfig();
console.log("Profile ID:", profile.id);populateSelectOptionsFromTaxonomy(selectEl, profile, optionsFrom)
Peuple un <select> avec des options depuis la taxonomie du profil.
Chemins supportés :
'taxonomy.categories': Toutes les catégories'taxonomy.categories[*].subcategories': Toutes les sous-catégories
const selectEl = document.createElement("select");
const profile = GeoLeaf._UIDomUtils.getActiveProfileConfig();
GeoLeaf._UIDomUtils.populateSelectOptionsFromTaxonomy(selectEl, profile, "taxonomy.categories");Module 3 : GeoLeaf.UI.Notifications (notifications.ts)
Rôle
Système de toast notifications avec animations et auto-dismiss.
API Principale
init(config)
Initialise le système de notifications.
Config :
container: Sélecteur du conteneur (défaut:'#gl-notifications')maxVisible: Nombre max de toasts visibles (défaut: 3)durations: Durées par type (ms)position: Position ('bottom-center','top-right', etc.)animations: Activer animations (défaut: true)
GeoLeaf.UI.Notifications.init({
container: "#gl-notifications",
maxVisible: 5,
position: "top-right",
durations: {
success: 2000,
error: 7000,
},
});success(message, duration?)
GeoLeaf.UI.Notifications.success("Données chargées avec succès !");
GeoLeaf.UI.Notifications.success("Sauvegarde réussie", 5000);error(message, duration?)
GeoLeaf.UI.Notifications.error("Impossible de charger les données");
GeoLeaf.UI.Notifications.error("Erreur réseau", 10000);warning(message, duration?)
GeoLeaf.UI.Notifications.warning("Connexion instable");info(message, duration?)
GeoLeaf.UI.Notifications.info("Chargement en cours...");clear()
GeoLeaf.UI.Notifications.clear();Structure HTML générée
<div id="gl-notifications" class="gl-notifications gl-notifications--bottom-center">
<div class="gl-toast gl-toast--success" role="alert" aria-live="polite">
<div class="gl-toast__icon">✓</div>
<div class="gl-toast__content">
<div class="gl-toast__message">Données chargées avec succès !</div>
</div>
<button class="gl-toast__close" aria-label="Fermer">×</button>
</div>
</div>Module 4 : Contrôles MapLibre (controls.ts)
Rôle
Agrège les contrôles MapLibre personnalisés. Chaque contrôle est défini dans son propre fichier.
API via GeoLeaf.UI
// Fullscreen
GeoLeaf.UI.initFullscreenControl(map, container);
// Geolocation
GeoLeaf.UI.initGeolocationControl(map, {
position: "topleft",
enableHighAccuracy: true,
});
// Add POI button (requires plugin-addpoi)
GeoLeaf.UI.initPoiAddControl(map, { position: "topright" });
// Theme toggle integrated in the map
GeoLeaf.UI.initThemeToggleControl(map, { position: "topright" });Configuration dans profile.json
{
"ui": {
"controls": {
"fullscreen": true,
"geolocation": true,
"themeToggle": true
},
"showCoordinates": true,
"showScale": true,
"scaleType": "numeric"
}
}Module 5 : Système de filtrage (filter-panel/)
Architecture
Le système de filtrage est divisé en modules spécialisés :
filter-panel/
├── filter-panel-accordion.ts // Accordéons du panneau filtres
├── lazy-loader.ts // Chargement lazy des composants filtres
├── proximity-manual-mode.ts // Saisie manuelle de la position de proximité
├── proximity-state.ts // État géolocalisation pour filtre proximité
├── shared.ts // Utilitaires partagés
└── svg-helpers.ts // Génération icônes SVG inlineComposants UI générés
1. Recherche textuelle
<div class="gl-filter-control gl-filter-control--search">
<label for="search-input">Recherche</label>
<input type="text" id="search-input" placeholder="Rechercher..." />
</div>2. Sélecteur de catégorie
<div class="gl-filter-control gl-filter-control--select">
<label for="category-select">Catégorie</label>
<select id="category-select">
<option value="">— Tous —</option>
<option value="restaurant">Restaurants</option>
<option value="hotel">Hôtels</option>
</select>
</div>3. Checkboxes multi-sélection
<div class="gl-filter-control gl-filter-control--checkboxes">
<label>Tags</label>
<div class="gl-filter-checkboxes">
<label><input type="checkbox" value="parking" /> Parking</label>
<label><input type="checkbox" value="wifi" /> WiFi</label>
</div>
</div>4. Filtre de proximité
<div class="gl-filter-control gl-filter-control--proximity">
<label>Proximité</label>
<input type="range" min="0" max="5000" step="100" value="1000" />
<span class="proximity-value">1.0 km</span>
</div>5. Tags actifs
<div class="gl-filter-active-tags">
<span class="gl-filter-tag" data-filter-type="category" data-filter-value="restaurant">
Restaurant
<button class="gl-filter-tag__remove">×</button>
</span>
<button class="gl-filter-clear-all">Tout effacer</button>
</div>État des filtres
const filterState = {
search: "restaurant",
category: "food",
subcategory: "",
tags: ["parking", "wifi"],
proximity: {
enabled: true,
radius: 1000, // metres
center: [48.8566, 2.3522],
},
};Thématisation CSS
Tous les composants UI utilisent des classes CSS BEM pour faciliter la personnalisation.
Variables CSS
:root {
/* Notifications */
--gl-toast-success: #10b981;
--gl-toast-error: #ef4444;
--gl-toast-warning: #f59e0b;
--gl-toast-info: #3b82f6;
/* Accordéons */
--gl-accordion-header-bg: #f5f5f5;
--gl-accordion-header-bg-hover: #e0e0e0;
--gl-accordion-border: #ddd;
/* Symboles */
--gl-symbol-size: 16px;
--gl-symbol-border: #666;
/* Filtres */
--gl-filter-bg: white;
--gl-filter-border: #ddd;
--gl-filter-tag-bg: #3b82f6;
--gl-filter-tag-color: white;
}
/* Dark theme */
[data-theme="dark"] {
--gl-accordion-header-bg: #2c3e50;
--gl-accordion-header-bg-hover: #34495e;
--gl-accordion-border: #555;
--gl-filter-bg: #2c3e50;
--gl-filter-border: #555;
}Intégration inter-modules
Exemple 1 : Légende avec accordéons
// In legend-renderer.ts
const { accordionEl, bodyEl } = GeoLeaf._UIComponents.createAccordion(container, {
layerId: "parcs",
label: "Parcs et Jardins",
collapsed: false,
visible: true,
});
const items = [
{ label: "Parc urbain", color: "#228B22" },
{ label: "Jardin public", color: "#90EE90" },
];
items.forEach((item) => {
const itemEl = document.createElement("div");
itemEl.className = "gl-legend__item";
bodyEl.appendChild(itemEl);
GeoLeaf._UIComponents.renderCircleSymbol(itemEl, {
fillColor: item.color,
radius: 8,
});
const labelEl = document.createElement("span");
labelEl.className = "gl-legend__item-label";
labelEl.textContent = item.label;
itemEl.appendChild(labelEl);
});Exemple 2 : Notifications après chargement
// After loading a GeoJSON layer (internal module)
fetch(layerUrl)
.then(() => {
GeoLeaf.UI.Notifications.success('Couche "Parcs" chargée avec succès !');
})
.catch((error) => {
GeoLeaf.UI.Notifications.error(`Erreur de chargement : ${error.message}`);
});Limitations
- Notifications : Maximum 3 toasts simultanés (configurable via
maxVisible) - Accordéons : Pas de support d'imbrication multiple (accordéons dans accordéons)
- Symboles : Icônes SVG requièrent sprite SVG défini
- Filtres de proximité : Calcul côté client (peut être lent avec +10k POIs)
Modules liés
- GeoLeaf.Legend : Utilise accordéons et symboles
- GeoLeaf.LayerManager : Utilise composants UI
- GeoLeaf.Filters : Système de filtrage backend
- GeoLeaf.POI : Panel builder pour affichage POI
Version : 2.0.0
Dernière mise à jour : mars 2026
