Skip to content

feat(drawing) : Refonte du contrôle Drawing - #490

Open
MatRouillard wants to merge 69 commits into
mainfrom
feature/drawing-refonte
Open

feat(drawing) : Refonte du contrôle Drawing#490
MatRouillard wants to merge 69 commits into
mainfrom
feature/drawing-refonte

Conversation

@MatRouillard

@MatRouillard MatRouillard commented Feb 20, 2026

Copy link
Copy Markdown
Collaborator

PR important de nombreux changements, principalement sur la partie dessin et édition de style, mais aussi en terme de dépendances (donc à confirmer sur la faisabilité @elias75015 @lowzonenose) :

Exemples :

Via la commande npm run sample:modules, les exemples se trouvent dans le dossier :

DrawingInteraction

Dossier : DrawingInteraction

StyleDialog

Dossier : StyleDialog

Ajouté

Modification dépendances / CSS

  • Ajout de fichiers SCSS avec dépendances correspondantes :
    • "sass": "^1.95.1",
    • "sass-loader": "^16.0.
    • "style-loader": "^4.0.0",
  • Màj de la dépendance css-loader: "css-loader": "^7.1.2";
  • Ajout de mixins scss dans le fichier src/packages/CSS/mixins.scss, s'appuyant notamment sur le DSFR pour avoir les mixins suivants :
    • hover-media-query : Méthode pour le hover sur les boutons (inactifs en mobile);
    • button-state($text-color, $background) : Permet la modification d'un bouton, avec :hover et :active compris (nécessaire d'instancier au préalable les variables --hover-tint et --active-tint);
    • respond-from : Méthode générique pour appliquer des styles en fonctions des breakpoint (Points de rupture DSFR);

Modification contrôles et interactions "génériques"

  • Modification du composant générique Control.js :

    • Ajout d'un écouteur d'événement sur la taille de la carte, avec deux composantes CSS :

      • --map-height : hauteur de la carte, en pixel;
      • --map-width : largeur de la carte, en pixel;

      L'écouteur d'événement est ajouté lorsque le premier contrôle est ajouté à une carte.

    • Ajout des méthodes abstraites et génériques _initialize, _initContainer et _initEvents, utilisé par la suite dans les autres contrôles (pas d'appel dans le constructeur pour ne pas poser de conflit avec les contrôles existant)

  • Ajout de méthodes dans le fichier Helper.js :

    • setIcon : ajoute une icône via une classe, un svg ou autre chose.
  • Ajout de composants "génériques" pour la créations d'autres composants, pour l'instant dans un dossier Toggle, à savoir :

    • Toggle.js : Contrôle correspondant à un bouton simple sur la carte, avec état activé ou non. Sert surtout de base pour les autres type de toggle.
    • ToggleInteraction.js : Contrôle liant un bouton avec une interaction sur la carte. L'interaction est activé / désactivé en fonction de l'état du bouton.
    • ToggleContent.js : Contrôle liant un bouton à un panneau / modale sur la carte. Cette modale est une instance de la classe Dialog.js, mais elle permet d'avoir des "helper" pour modifier facilement le dialog.
    • Dialog.js : Contrôle créant un panneau sur la carte, positionné à gauche ou à droite et ayant 3 variantes de tailles. Le panneau comprend un titre avec une icône (optionnelle) et un contenu. Pour les panneaux nécessitant une navigation tertiaire, il est possible de l'ajouter via l'options items, correspondant au contrôle TabNav, contenant lui même desTabNavItem`.
    • TabNavItem.js : Contrôle correspondant à un élément de la navigation tertiaire. Possède un titre, une icône (optionnelle) et un contenu. Une fonction peut être lancée à l'ouverture ou à la fermeture de l'élément. N'a pas besoin d'être (voire ne doit pas être) ajouté à la carte via la méthode setMap.
    • TabNav.js : Contrôle correspondant à une navigation tertiaire. Gère notamment le fait qu'un seul élément ne peut être ouvert à la fois.
  • Ajout d'interaction (dossier Interactions) étendant les interactions natives openlayer :

    • Drawing.js : étend l'interaction Draw. Permet notamment de gérer le style (défini dans selectStyle.js et selectFlatStyle.js, le dernier utilisant un flatStyle pour gérer le style), mais aussi de gérer des raccourcis claviers et de lier cela à une sélection.
    • LongTouch.js : nouvelle interaction permettant de gérer un événement de type "longtouch".
    • Modifying.js : étend l'interaction Modify. Gère notamment les liens avec une sélection donnée, les interactions de type "longTouch" ou même de double click par exemple. Permet aussi de gérer des raccourcis claviers et d'afficher un menu simple au clic droit.
    • Selecting.js : étend l'interaction Select. Gère notamment le double click sur un événement sélectionné, ajoute une méthode clear pour effacer la sélection avec un envoi d'événement et gère le style par défaut de la sélection.

Ajout contrôle pour le dessin

  • Ajout d'un composant de dessin Draw.js :

    • Hérite du contrôle ToggleContent, et contient une liste d'intéractions, qui sont ajoutées comme étant des ToggleInteraction pour pouvoir les activer et désactiver facilement.
    • Lié à une sélection; par défaut en créé une si aucune n'est donné en paramètre.
    • ⚠️ Pour le moment le footer n'a pas encore été initialisé et n'est pas initialisable facilement par injection de code ==> comportement à modifier pour avoir la sauvegarde d'un dessin par exemple ?
  • Ajout de composant affichant des informations, dans le dossier ContextMenu :

    • InfoControl.js pour afficher des informations sur la carte (notamment indications pour le dessin);
    • SimpleMenu.js, plus light et moins dense que le contrôle ContextMenu;

Ajouté ensuite

Modification du composant Dialog

Modification du contrôle Dialog.js pour ajouter des boutons de footer via l'attribut footer et la méthode setFooterContent.
Il est possible d'ajouter des boutons via footer.buttons et du contenu au dessus, via footer.content.

Modification du composant de dessin

Refonte du contrôle de dessin Draw.js, avec intégration d’un panneau de contenu, d’un groupe de boutons d’interactions et d'un comportement par défaut :

  • Gestion d’interactions de dessin personnalisées via options, ou ajout automatique des interactions par défaut Point, Ligne, Surface;
  • Liaison native à une interaction de sélection, avec création d’une sélection par défaut si aucune n’est fournie;
  • Gestion de source de dessin, avec création d’une source par défaut si absente;
  • Possibilité d’ajouter une couche automatiquement sur la carte;
  • Intégration du dialogue de style optionnel StyleDialog.js, via l'option style:true dans le constructeur :
    • Ouverture du dialogue selon la sélection;
    • Initialisation des formulaires à partir du style de la feature sélectionnée;
    • Gestion du style selon l'usage, via le paramètre onStyle :
      • Par défaut : application du flat style aux features sélectionnées;
      • onStyle:false : remontée des événements "style" uniquement;
      • onStyle: (property, value, features) => ... : application de la fonction sur les entités sélectionnées;
  • Style non-DSFR pris en charge.

Inputs de style

Ajout d'input de style, héritant de la classe générique Control.js.
Le but est de lier une propriété (à priori propriété flat-style, par exemple "fill-color"), avec un input conforme aux attentes (pas simplement une sélection).
Les fichiers sont trouvables dans le dossier Input.
La gestion des styles a été faites pour le mode DSFR / non-DSFR directement dans le dossier CSS Input, avec une gestion commune et une gestion par thème.

Les contrôles créées sont :

DefaultInput

Composant de base pour les champs de formulaire de style DefaultInput.js :

  • Support du label, de l’info de label, de la propriété flat style, du type et de l’état disabled;
  • Émission des changements de valeur pour permettre la propagation dans les formulaires de style;
  • Support d’attributs HTML additionnels, via l'attribut attributes, ajouté directement à l'input ou à la sélection;

InputNumber

Extension de DefaultInput dédiée aux valeurs numériques (<input type="number">) InputNumber.js :

  • Synchronisation des changements avec émission d’événement;
  • Ajout de boutons incrément / décrément avec gestion d’appui long (DSFR uniquement);

CustomSelect

Select personnalisé accessible, basé sur un modèle combobox CustomSelect.js :

  • Navigation clavier avancée (voir exemple de W3C pour l'inspiration);
  • Gestion du focus, de l’ouverture, de la fermeture, de la sélection et de la recherche clavier;
  • Comportement mobile avec panneau d’options redimensionnable par glisser;

CustomSelectGrid

Extension de CustomSelect en mode grille CustomSelectGrid.js :

  • Navigation clavier adaptée au déplacement bidimensionnel;

InputColor

Extension de CustomSelectGrid dédiée aux couleurs InputColor.js, créée pour faciliter l'utilisation des couleurs (devra être amélioré à terme) :

  • Palette par défaut fournie, avec option Sans couleur;
  • Mise à jour visuelle de la couleur sélectionnée dans le composant;
  • Compatibilité avec l’initialisation de valeur pour préremplissage formulaire;

Gestion de formulaire de style

Ajout d'une classe pour le formulaire de style, pouvant contenir des inputs / select basiques HTML (toujours liés à des propriétés flatStyle) ou des input customisés, soit de type DefaultInput, soit d'un autre type, à condition d'avoir les méthodes getInput() et getElement().
Les fichiers sont trouvables dans le dossier StyleDialog, les fichiers CSS dans CSS/Controls/StyleDialog.
Les classes et instances créées sont :

FlatStyleForm

Classe générique de style flat OpenLayers FlatStyleForm.js :

  • Ajout dynamique de champs selon type : number, color, pattern, default, textarea, select...;
  • Possibilité d'ajouter un input différent via form.addInput(config), avec en paramètre config.input correspondant à un objet ayant une méthode getInput et getElement
  • Gestion unifiée des changements avec émission d’événements style;
  • Support de préremplissage via setFlatStyle;
  • Support de séparateurs visuels dans le formulaire;
  • Affichage dynamique des champs en fonction des propriétés et du type d'objet sélectionné (voir GPFflatStyleForm.scss pour plus d'informations sur la gestion de cela).

styleForm

Instance de FlatStyleForm.js, pour la gestion du style des entités styleForm.js :

  • Gestion du style de point;
  • Gestion du remplissage;
  • Gestion du contour;
  • Gestion des options de ligne;
  • Couverture des paramètres couleur, opacité, largeur de trait, dash, rayon, etc.;
  • Présence de certaines options encore désactivées, en attente de prise en charge complète, liés notamment à l'éditeur carto (gestion des flèches, motifs etc.);

labelForm

Instance de FlatStyleForm.js, pour une gestion simple des étiquettes labelForm.js :

  • Gestion de la valeur du texte;
  • Gestion de la couleur du texte;
  • Gestion de la taille du texte;
  • Mise à jour du texte avec délai pour limiter les rafales de changements;

StyleDialog

Classe héritant de Dialog.js, permettant de lier le dialogue de style avec une sélection StyleDialog.js :

  • Chargement automatique des formulaires par défaut si aucun formulaire n’est fourni (pour l'instant seulement styleForm.js et labelForm.js, chacun mis dans des onglets de la navigation tertiaire);
  • Ouverture et fermeture selon l’état de la sélection;
  • Propagation des événements style des formulaires vers le dialogue;
  • Exposition de helpers pour récupérer un input et injecter des valeurs de formulaire;

À améliorer / modifier

  • Compléter les options actuellement présentes mais désactivées dans le formulaire de style (forme du point par exemple);
  • Gestion du style faite entièrement dans le contrôle Draw, alors qu'elle devrait être faite dans un fichier à part pour bien différencier gestion du style et gestion du dessin;
  • Ajouter exemple pour l'ajout de bouton dans le contrôle de dessin.

@elias75015

Copy link
Copy Markdown
Contributor

Premiers retours :
J'ai ajouté un exemple avec paramétrage par défaut et deux autres widgets : https://localhost:8080/samples/tests/DrawingInteraction/pages-ol-drawinginteraction-modules-dsfr-default.html

1 - Le widget ne reprend pas la logique de positionnement globale par "coin" (top-left, top-right...) des autres widgets, du coup il ne s'intègre pas correctement à la pile de boutons dans le container adéquate (div d'id "position-container-top-right" en l'occurence)

image

2 - Le widget hérite du composant toggle. Du coup, l'id des éléments qui compose le widget draw de suit pas non plus la logique des autres widgets en terme UID et de nommage :

image Là ou pour les autre widgets on a quelque chose : image

3 - Avec le paramétrage par défaut, les interactions permettant de modifier les features dessinées devraient être actives

@iamvdo

iamvdo commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Hello @MatRouillard.
J'ai commencé à regarder vite fait, mais les exemples sont cassés: ton commit 2dd09f6 a supprimé la méthode getStyle, du coup on a des drawing[0].getStyle is not a function ou drawing.Point.getStyle is not a function
(j'ai regardé que les 2 premiers exemples du coup)

@MatRouillard

Copy link
Copy Markdown
Collaborator Author

@iamvdo Bien vu, on les utilisait avant mais ça empêchait d'avoir une symbolisation par défaut. JE ne les avais pas enlevé des exemples.
Par ailleurs, je t'invite plutôt à regarder les exemples sur : https://localhost:8080/samples/tests/StyleDialog plutôt que ceux dont tu mentionnais (j'imagine que tu faisais ceux sur https://localhost:8080/samples/tests/DrawingInteraction) car je les avais fait au début et sur la partie contrôle, la configuration par défaut n'est pas forcément bien faite et donc l'exemple pas très utile ?

@iamvdo

iamvdo commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Alors oui effectivement, il y a beaucoup de pages de démo, et j'avoue ne pas comprendre ce qu'on doit regarder dans chaque page, ni ce qui est vraiment testé, entre DrawingInteraction et StyleDialog.

Voilà quelques points en vrac:

  • de mon coté, je pense que l'initialisation de Draw par défaut devrait être l'inverse de maintenant: donc avec style: true et addToMap: true, que l'on peut désactiver si l'on souhaite contrôler plus finement le plugin (en relisant, ça doit correspondre au point 3 d'@elias75015 ?). D'ailleurs addToMap n'a pas de valeur par défaut.
  • tu calcule les propriétés CSS --map-width et --map-height, mais je ne vois pas d'utilisation de ces variables ? J'avais réagis ici à ça: feat(css-tokens): Ajout fct calcul taille map et ajout css tokens #467 (comment)
  • pas compris ce que tu changes comme comportement là ? 6223255
  • coté éditeur, je vois que lorsque l'on dessine, l'objet est mieux symbolisé quand il est sélectionné. Ça se passe au niveau de ce nouveau widget ? Ou ailleurs ? Parce que encore une fois, pour un widget "clé en main", j'aurais vu ça dedans par défaut...

Ça serait pas mal qu'on essaie de l'intégrer coté entrée carto, on aura sûrement pleins d'autres retours ;) J'essaie de regarder dès que j'ai encore un peu de temps...

@MatRouillard

Copy link
Copy Markdown
Collaborator Author

@iamvdo

et j'avoue ne pas comprendre ce qu'on doit regarder dans chaque page, ni ce qui est vraiment testé, entre DrawingInteraction et StyleDialog

De base on avait fait le composant de dessin avec notamment des classes étendant les interactions de dessin d'openlayers (DrawingInteraction notamment) et dans le même temps j'avais essayé de créer une classe utilitaire pour les contrôles / boutons sur la carte (Toggle et les classes l'étendant).
Les exemples dans DrawingInteractions ont été fait dans une optique de test des différentes classes Toggle, en DSFR / non-DSFR, et pour tester les interactions de dessin, sans style associé. Les exemples pages-ol-toggle sont juste pour les toggle, sans interactions particulières derrière, les exemples pages-ol-drawinginteraction sont pour le coup + pour le contrôle de dessin (mais ça utilise les classes Toggle).

Les exemples dans StyleDialog sont pour le coup centrés sur la gestion du style, avec le composant de dessin. default correspond au comportement par défaut (ou presque), draw-with-style à celui avec le composant StyleDialog directement intégré dans le contrôle et draw-with-export un exemple avec des boutons / inputs, correspondant à un besoin côté entrée carto.

de mon coté, je pense que l'initialisation de Draw par défaut devrait être l'inverse de maintenant.

Ok, je ne pense pas que ça pose trop de problème de toute manière

tu calcule les propriétés CSS --map-width et --map-height, mais je ne vois pas d'utilisation de ces variables ?

Je les ai utilisé dans la définition du max-width du dialog (voir GPFpanel.scss). Après je n'en fais pas une utilisation folle en soit.

J'avais vu le commentaire mais je ne me suis pas vraiment penché dessus. J'avais essayé de voir comment tu l'avais utilisé mais la PR était trop grosse pour que je regarde rapidement et en comprenant donc je l'ai laissé de côté. Mais du coup je pense que ça serait intéressant d'utiliser les container en effet.

pas compris ce que tu changes comme comportement là ? 6223255

En fait j'aurais en soit dû faire une autre branche + une PR, car j'ai changé le comportement par défaut du layerswitcher pour résoudre plus facilement / plus proprement un ticket de l'éditeur concernant le tooltip à afficher (voir [BUG] Gestionnaire de couche #141). Comme sur l'éditeur le layerswitcher n'est pas positionné dans une barre, le tooltip ne s'affichait pas. J'ai donc ajouté un calcul en js pour savoir si le tooltip devait s'afficher à gauche ou à droite, et j'ai fait en sorte qu'il s'affiche aussi lorsque l'élément n'était pas positionné. S'il faut l'enlever je peux le faire, juste ça me paraîssait plus propre que de faire des surcharges CSS côté éditeur.

côté éditeur, je vois que lorsque l'on dessine, l'objet est mieux symbolisé quand il est sélectionné. Ça se passe au niveau de ce nouveau widget ? Ou ailleurs ? Parce que encore une fois, pour un widget "clé en main", j'aurais vu ça dedans par défaut...

Oui c'est lié à la gestion du style. Comme la gestion du style était un peu complexe pour être gérée facilement sur les extensions (i.e. sans une grosse fonction de gestion du style), j'ai passé style:null dans la sélection, ce qui permet de ne pas modifier le style des objets sélectionnés lorsqu'on les sélectionne, et donc de pouvoir modifier leur style par défaut. Le contrecoup c'est qu'il n'y a aucun style affiché pour un objet sélectionné. Je ne sais pas trop comment faire ça facilement, mais cela entendrait d'avoir une gestion plus propre du style plutôt qu'une gestion simple du style par défaut.

@MatRouillard

Copy link
Copy Markdown
Collaborator Author

@elias75015 @iamvdo j'ai modifié le point sur lequel vous étiez d'accord, à savoir le addToMap avec une valeur par défaut et style, vrai par défaut. J'ai aussi ajouté deux autres paramètres :

  • snap=false : si vrai, ajoute une interaction Snap (voir Snap). Possible d'en donner une aussi pour qu'elle soit ajoutée.
  • modify=true : ajoute une interaction de modification (sauf si faux).

Comment thread src/packages/Controls/Input/CustomSelect.js Fixed
Comment thread src/packages/Controls/Input/CustomSelect.js Fixed
Comment thread src/packages/Controls/Draw/Draw.js Fixed
Comment thread src/packages/Controls/Input/CustomSelect.js Fixed
Comment thread src/packages/Controls/Input/CustomSelect.js Fixed
Comment thread src/packages/Controls/Draw/Draw.js Fixed
@elias75015

Copy link
Copy Markdown
Contributor

Positionnement semble OK.

Il semble y avoir encore un problème de séparateur différent avec les boutons adjacents
image

@elias75015

Copy link
Copy Markdown
Contributor

Le style du bouton en mode "actif" est différent des autres boutons :

Capture d’écran du 2026-05-12 11-56-53

@elias75015

Copy link
Copy Markdown
Contributor

Autre problème, le widget ne semble pas s'intégrer à la logique de "un seul panel ouvert par côté". C'est géré par le fichier src/packages/Utils/PanelManager.js

Capture.video.du.12-05-2026.11.59.25.webm

@MatRouillard

MatRouillard commented May 12, 2026

Copy link
Copy Markdown
Collaborator Author

@elias75015 j'ai fait les modifs pour le panneau (logique de "un seul panel ouvert par côté").
J'ai par contre du modifier le fichier PanelManager.js#L23 pour que la vérification se fasse à la fois sur l'attribut aria-pressed (tel que c'est fait actuellement) mais aussi sur l'attribut aria-extended (attribut ARIA que j'ai utilisé pour les toggle, puisqu'ils contrôlent un dialogues et n'ont pas seulement un état "pressé / non pressé"). Ça peut potentiellement être cassant, même si je ne pense pas que ça le soit.

Je fais les modifications concernant le bandeau bleu dans un autre commit.

@elias75015

Copy link
Copy Markdown
Contributor

Nous n'utilison pas "aria-extented", ni côté extensions, ni côté entee carto, donc a priori pas de problème

@MatRouillard

Copy link
Copy Markdown
Collaborator Author

@elias75015 comme tu me l'avais proposé, j'ai fait des modifs pour passer tous les widgets avec la couleur $background-open-blue-france, plutôt que la barre bleue. J'ai ajouté un fichier SCSS plutôt que faire tout en CSS car j'utilise des mixins notamment pour gérer l'état hover / actif en mobile. Si c'est ok pour toi je laisse, sinon je modifierais le toggle pour lui mettre la barre bleue aussi et on surchargera côté éditeur carto.

MatRouillard and others added 26 commits September 2, 2026 10:04
…autres contrôles

Le format KML prend désormais en compte les array de style. Les contrôles Toggle ont été adaptés pour avoir une compatibilité avec les autres (ex: Export)
Ajout d'une méthode setGeom sur la classe FlatStyleForm pour gérer l'affichage du formulaire. Gère mieux la sélection pour pouvoir sélectionner plusieurs entitès en même temps
@iamvdo
iamvdo force-pushed the feature/drawing-refonte branch from 03c1cb3 to 0196393 Compare September 3, 2026 08:14
@iamvdo

iamvdo commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Salut @MatRouillard.
J'ai découvert un "bug" en testant l'intégration dans l'entrée carto: un calque est ajouté dès l'ajout du contrôle à la map, ce qui pose soucis dans le cas d'ajout de calques de manière asynchrone (il se retrouve possiblement en dessous). Mais surtout, il serait préférable d'ajouter le calque uniquement au moment ou l'on commence un dessin, pas dès le départ. J'ai tenté l'option addToMap: false qui n'ajoute pas ce calque par défaut, mais qui ne l'ajoute pas plus tard non plus...
Tu peux me dire comment tu voyais les choses ?

Aussi, j'en ai profité pour rebaser cette branche sur la main, et j'ai force push, donc attention à bien récupérer la bonne version de branche

@MatRouillard

MatRouillard commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

Tu peux me dire comment tu voyais les choses ?

Il me semble que la demande était justement d'avoir la config la plus simple possible, et donc d'avoir la possibilité, comme les autre contrôles, d'ajouter un contrôle à la carte avec addToMap: true. Le but était d'avoir un contrôle "autoporté" justement.
Pour moi il n'était pas question ici de gestion synchrone / asynchrone des couches, seulement d'une question pratique. Cela explique pourquoi addToMap: false n'ajoute pas non plus de couche de dessin plus tard.

@iamvdo

iamvdo commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Je n'ai pas très bien compris le addToMap en fait :P
Mais dans tous les cas, là ça va au delà de la config: en tant qu'utilisateur d'une app carto, je ne devrais pas voir ce calque tant que je n'ai pas encore dessiné qqch. Sinon ça m'oblige à le "gérer", alors même que je ne sais pas à quoi il sert. Aussi, si je le supprime, il n'est plus ajouté (donc le widget ne fonctionne plus)
Et si, dans mon app, j'ajoute une couche au layerswitcher, il y a de fortes chances que ce layer se retrouve en dessous

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dessin : reste à faire UX/UI sur le widget de dessin

4 participants