Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DRAFT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Mise à jour des icônes DSFR pour les boutons pictogrammes des widgets

- Contextual Menu : Ajout du getFeatureInfo dans les entrées du menu contextuel (#442)
- LayerSwitcher : Gestion des noms des styles MapBox issus de cartes.gouv.fr (#455)
- Territories : Mode DragNDrop des territoires (#468)

* 🔨 [Changed]

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "geopf-extensions-openlayers",
"description": "French Geoportal Extensions for OpenLayers libraries",
"version": "1.0.0-beta.6-464",
"date": "24/11/2025",
"version": "1.0.0-beta.6-468",
"date": "30/11/2025",
"module": "src/index.js",
"directories": {},
"engines": {
Expand Down
75 changes: 60 additions & 15 deletions src/packages/Controls/Territories/Territories.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ class Territories extends Control {
this.getContainer().classList.add("gpf-button-no-gutter");
}

// draggable des vues
var self = this;
this.createDraggableElement(
self.element.querySelector("#gpf-territories-views-listview-entries-id"),
self
);

/**
* event triggered when a territory is loaded
*
Expand Down Expand Up @@ -211,10 +218,7 @@ class Territories extends Control {
var view = this._createTerritoryView(territory);
if (entry && view) {
this.panelTerritoriesEntriesContainer.appendChild(entry);
var panelTerritoriesViewsContainer = this.element.querySelector("#gpf-territories-views-listview-entries-id");
if (panelTerritoriesViewsContainer) {
panelTerritoriesViewsContainer.appendChild(view);
}
this.panelTerritoriesViewsContainer.appendChild(view);
var count = this.element.querySelector("#gpf-territories-views-count-id");
if (count) {
var nb = this.territories.filter(t => !t.isRemoved).length + 1;
Expand All @@ -224,6 +228,7 @@ class Territories extends Control {
isRemoved : false,
isAdded : isAdded,
data : territory,
initialIndex : this.territories.length,
domEntry : entry,
domView : view
});
Expand All @@ -249,7 +254,7 @@ class Territories extends Control {
* Remove a territory
*
* @param {String} territory - territory id (FRA, MTQ, ...)
* @param {Boolean} [force=false] - force removal even if territory was added manually
* @param {Boolean} [force=false] - force removal
* @returns {Boolean} - true|false
* @public
* @example
Expand All @@ -261,14 +266,16 @@ class Territories extends Control {
for (let i = 0; i < this.territories.length; i++) {
const o = this.territories[i];
if (o.data.id === territory.data.id) {
this.territories[i].domEntry.remove();
this.territories[i].domView.remove();
if (!force) {
// on ne le supprime pas de la liste des territoires
// mais on le masque uniquement
this.territories[i].isRemoved = true;
this.territories[i].domEntry.classList.add("gpf-hidden");
this.territories[i].domView.style.display = "none";
} else {
// on le supprime de la liste des territoires
this.territories[i].domEntry.remove();
this.territories[i].domView.remove();
this.territories.splice(i, 1);
}
found = true;
Expand Down Expand Up @@ -402,6 +409,7 @@ class Territories extends Control {
* isAdded : { Boolean },
* domView : { HTMLelment },
* domEntry : { HTMLelment },
* initialIndex : { Number }, // index d’origine
* data : {
* id: "MTQ",
* title: "Martinique",
Expand Down Expand Up @@ -434,6 +442,8 @@ class Territories extends Control {
this.containerTerritoriesOptions = null;
/** @private */
this.panelTerritoriesEntriesContainer = null;
/** @private */
this.panelTerritoriesViewsContainer = null;
}

/**
Expand All @@ -460,6 +470,7 @@ class Territories extends Control {
// menu views button
var territoriesPanelMenuViewsDiv = this._createTerritoriesPanelMenuViewsDivElement();
territoriesPanel.appendChild(territoriesPanelMenuViewsDiv);
this.panelTerritoriesViewsContainer = territoriesPanelMenuViewsDiv.querySelector("#gpf-territories-views-listview-entries-id");

// INFO
// Les territoires seront ajoutés dans ce conteneur
Expand Down Expand Up @@ -788,23 +799,31 @@ class Territories extends Control {
logger.trace(e);
// On supprime tous les territoires ajoutées manuellement via le bouton "Ajouter une vue"
// et on recharge la liste initiale
var territories = [...this.territories];
for (let index = 0; index < territories.length; index++) {
const territory = territories[index];
for (let index = 0; index < this.territories.length; index++) {
const territory = this.territories[index];
Comment on lines +802 to +803
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterating through this.territories while calling removeTerritory(territory, true) modifies the array during iteration (via splice at line 279), causing elements to be skipped. Use a reverse loop or iterate over a copy of the array.

Suggested change
for (let index = 0; index < this.territories.length; index++) {
const territory = this.territories[index];
for (const territory of this.territories.slice()) {

Copilot uses AI. Check for mistakes.
if (territory.isAdded) {
// on supprime les territoires ajoutés manuellement
this.removeTerritory(territory, true);
} else {
// on ré-affiche les territoires non supprimés
if (territory.isRemoved) {
this.panelTerritoriesEntriesContainer.appendChild(territory.domEntry);
var panelTerritoriesViewsContainer = this.element.querySelector("#gpf-territories-views-listview-entries-id");
if (panelTerritoriesViewsContainer) {
panelTerritoriesViewsContainer.appendChild(territory.domView);
}
territory.domEntry.classList.remove("gpf-hidden");
territory.domView.style.display = "";
territory.isRemoved = false;
}
}
}
// reordonne la liste des territoires selon l'index initial
this.territories.sort((a, b) => a.initialIndex - b.initialIndex);
// mise à jour le DOM principal
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected 'mise à jour le' to 'mise à jour du' for proper grammar.

Suggested change
// mise à jour le DOM principal
// mise à jour du DOM principal

Copilot uses AI. Check for mistakes.
this.panelTerritoriesEntriesContainer.innerHTML = "";
this.territories.forEach(territory => {
this.panelTerritoriesEntriesContainer.appendChild(territory.domEntry);
});
this.panelTerritoriesViewsContainer.innerHTML = "";
this.territories.forEach(territory => {
this.panelTerritoriesViewsContainer.appendChild(territory.domView);
});
// mise à jour du compteur des vues
var count = this.element.querySelector("#gpf-territories-views-count-id");
if (count) {
Expand Down Expand Up @@ -857,6 +876,32 @@ class Territories extends Control {
}
}

/**
* ...
* @param {Event} e - ...
Comment on lines +880 to +881
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSDoc comment for onReorderTerritoriesViews is incomplete. It should describe what the method does (reorders territories based on drag-and-drop interaction) and specify the type of event parameter (e.g., Sortable event object).

Suggested change
* ...
* @param {Event} e - ...
* Reorders the territories based on the current order of views in the container,
* typically after a drag-and-drop interaction.
* @param {Event} e - The drag-and-drop event object (e.g., from a Sortable or Draggable interaction).

Copilot uses AI. Check for mistakes.
* @private
*/
onReorderTerritoriesViews (e) {
logger.trace(e);

// on récupère l’ordre des vues dans le conteneur
const domViews = Array.from(this.panelTerritoriesViewsContainer.children);
// on recrée l’ordre des territoires selon les vues
const newOrderTerritories = [];
domViews.forEach(domView => {
const found = this.territories.find(territory => territory.domView === domView);
if (found) {
newOrderTerritories.push(found);
}
});
this.territories = newOrderTerritories;
// mise à jour l’ordre des territoires dans le DOM principal
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected 'mise à jour' to 'mise à jour de' for proper grammar.

Suggested change
// mise à jour l’ordre des territoires dans le DOM principal
// mise à jour de l’ordre des territoires dans le DOM principal

Copilot uses AI. Check for mistakes.
this.panelTerritoriesEntriesContainer.innerHTML = "";
this.territories.forEach(territory => {
this.panelTerritoriesEntriesContainer.appendChild(territory.domEntry);
});
}

};

// on récupère les méthodes de la classe DOM
Expand Down
81 changes: 68 additions & 13 deletions src/packages/Controls/Territories/TerritoriesDOM.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Sortable from "sortablejs";

const stringToHTML = (str) => {
var support = function () {
if (!window.DOMParser) {
Expand Down Expand Up @@ -25,17 +27,37 @@ const stringToHTML = (str) => {
return dom;
};

var TerritoriesDOM = {

var TerritoriesDOM = {

/**
* Add uuid to the tag ID
* @param {String} id - id selector
* @returns {String} uid - id selector with an unique id
* Add uuid to the tag ID
* @param {String} id - id selector
* @returns {String} uid - id selector with an unique id
*/
_addUID : function (id) {
var uid = (this.uid) ? id + "-" + this.uid : id;
return uid;
},

/**
* https://github.com/SortableJS/Sortable?tab=readme-ov-file#options
* @param {*} elementDraggable - element to make draggable
* @param {*} context - context for the draggable element
* @returns {*} Sortable instance
*/
createDraggableElement : function (elementDraggable, context) {
const sortable = Sortable.create(elementDraggable, {
animation : 150,
handle : ".gpf-btn-icon-territories-draggable",
draggable : ".draggable-territories-view",
ghostClass : "gpf-territories-view-ghost",
onEnd : function (e) {
context.onReorderTerritoriesViews(e);
}
});
return sortable;
},

/**
* Main container (DOM)
Expand Down Expand Up @@ -407,7 +429,8 @@ var TerritoriesDOM = {
</div>
</div>
<div class="fr-messages-group" id="add-view-form-fieldset-messages" aria-live="polite">
<p class="fr-message fr-message--error gpf-hidden" id="${inputId}-message-error-id">Ce nom est déjà utilisé</p>
<p class="fr-message fr-message--error gpf-hidden" id="${inputId}-message-error-duplicate-id">Ce nom est déjà utilisé</p>
<p class="fr-message fr-message--error gpf-hidden" id="${inputId}-message-error-empty-id">Le nom est vide</p>
</div>
</fieldset>
</form>
Expand Down Expand Up @@ -441,6 +464,19 @@ var TerritoriesDOM = {
return;
}
button.setAttribute("aria-pressed", "false");
// reinit
var input = document.getElementById(inputId);
if (input) {
input.value = "";
var emptyMessage = document.getElementById(inputId + "-message-error-empty-id");
var duplicateMessage = document.getElementById(inputId + "-message-error-duplicate-id");
if (emptyMessage) {
emptyMessage.classList.add("gpf-hidden");
}
if (duplicateMessage) {
duplicateMessage.classList.add("gpf-hidden");
}
}
self.onCloseTerritoriesViewsClick(e);
}, false);
}
Expand All @@ -460,17 +496,36 @@ var TerritoriesDOM = {
e.preventDefault();
var input = document.getElementById(inputId);
if (input) {
var message = document.getElementById(inputId + "-message-error-id");
var territory = self.territories.find(e => e.data.title === input.value);
if (message && input.value === "") {
var emptyMessage = document.getElementById(inputId + "-message-error-empty-id");
var duplicateMessage = document.getElementById(inputId + "-message-error-duplicate-id");

// Check if input is empty
if (input.value === "") {
if (emptyMessage) {
emptyMessage.classList.remove("gpf-hidden");
}
if (duplicateMessage) {
duplicateMessage.classList.add("gpf-hidden");
}
return;
}
// validation du nom de la vue avec ceux de la liste
if (message && territory) {
message.classList.remove("gpf-hidden");
// Check if territory with same name exists
if (territory) {
if (duplicateMessage) {
duplicateMessage.classList.remove("gpf-hidden");
}
if (emptyMessage) {
emptyMessage.classList.add("gpf-hidden");
}
return;
} else {
message.classList.add("gpf-hidden");
}
// Hide both error messages if valid
if (emptyMessage) {
emptyMessage.classList.add("gpf-hidden");
}
if (duplicateMessage) {
duplicateMessage.classList.add("gpf-hidden");
}
self.onAddTerritoriesViewClick(e, input.value);
}
Expand Down Expand Up @@ -598,7 +653,7 @@ var TerritoriesDOM = {
var buttonId = "gpf-territories-view-entry-button-id-" + o.id;
if (o) {
var entry = stringToHTML(`
<div class="gpf-panel__views_territories-listview-entry">
<div class="gpf-panel__views_territories-listview-entry draggable-territories-view">
<!-- ${o.title} -->
<div style="display: flex;flex-direction: row;align-items: center;">
<span class="gpf-btn gpf-btn-icon-territories-draggable gpf-btn--tertiary"></span>
Expand Down