diff --git a/docs/config_json.rst b/docs/config_json.rst index 7299e83bf..57d88667a 100644 --- a/docs/config_json.rst +++ b/docs/config_json.rst @@ -12,7 +12,7 @@ Cette section détaille les configurations possibles pour le navigateur dans un contexe cartographique. À l'aide de fichiers de configuration (fichiers JSON) , il est possible de paramétrer: - - l'application + - l'application - le contenu cartographique @@ -27,8 +27,8 @@ Ce dernier est situé dans le répertoire : - `src/config/config.json `_ -Il est également possible de configurer l'application grâce à un second -fichier selon l'environnement désiré (test ou production). +Il est également possible de configurer l'application grâce à un second +fichier selon l'environnement désiré (test ou production). Pour modifier le comportement de l'application, vous pouvez modifier: @@ -77,9 +77,9 @@ Résumé - `Context`_ - .. line-block:: - Activation de l'API de context d'IGO2. - Cette API sera documentée - indépendamment de la présente + Activation de l'API de context d'IGO2. + Cette API sera documentée + indépendamment de la présente documentation. - ContextManager @@ -89,13 +89,13 @@ Résumé Permet d'activé ou non la capacité d'afficher un résumé de la position du curseur. Le résumé est dépendant des sources de recherche utilisées. Désactivé par défaut. - - + - * - `hasExpansionPanel`_ - Boolean - .. line-block:: Permet d'ouvrir un paneau d'expansion à partir d'un bouton situé dans le coin inférieur gauche - de la carte. + de la carte. Ce dernier contient les données tabulaires pour les données WFS / Vectorielle / Cluster ** Encore en développement ** @@ -108,17 +108,21 @@ Résumé - .. line-block:: Importation Exportation + * - interactiveTour + - `interactiveTour`_ + - Permet de configurer les tours interactifs de présentation de l'application. + - * - **language*** - `Language`_ - .. line-block:: - Chemin d'accès des fichiers de traduction de - l'application. - - Tous + Chemin d'accès des fichiers de traduction de + l'application. + - Tous * - mapOverlay - `MapOverlay`_ [] - .. line-block:: Éléments visuels à ajouter par dessus la carte de l'application. - - + - * - projections - :ref:`Projection ` [] - .. line-block:: @@ -140,7 +144,7 @@ Résumé Recherche Carte * - .. line-block:: - **Theme*** + **theme*** Voir: `theme`_ - String @@ -154,20 +158,30 @@ Résumé le `igo2-lib/packages/core/src/style/themes `_ - - * - Title - - String + * - title + - String - Permet de définir le titre de l'application qu'on retrouve dans le menu d'accueil. - - - * - Description + - + * - description - String - - Permet de définir ce qui sera affiché lors de la recherche dans les moteurs de recherche, comme par exemple Google. + - Permet de définir ce qui sera affiché lors de la recherche dans les moteurs de recherche, comme par exemple Google. + - + * - welcomeWindow + - `WelcomeWindow`_ + - .. line-block:: + Permet d'ouvrir une fenêtre d'accueil à l'arrivé dans application. Le contenu doit + être configurer à l'aide les fichiers de traduction en.json et fr.json. + "welcomeWindow": { + "html": "

Débutez en sélectionnant un contexte ★

", + "title": "Fenêtre d'accueil", + "closeButton": "Fermer", + "notShowCheck": " ne plus afficher"} - Important : Les propriétés en caractère gras suivies d'un * sont obligatoires. - *************** Analytics *************** @@ -268,12 +282,12 @@ Catalog .. line-block:: Cette section de la configuration permet de charger une liste de sources de cataloguage. Une fois les sources chargées, il est possible d'ajouter ces couches d'informations à la carte. - + Les sources de cataloguage permises: - - Service WMS + - Service WMS - Service WMTS - + Les couches d'informations contenues dans ces services sont récupérées grâce aux couches publiées dans le GetCapabilities du service. Dans la présente version @@ -362,7 +376,7 @@ Propriétés .. list-table:: :widths: 10 10 30 15 10 :header-rows: 1 - + * - .. line-block:: Propriétés - .. line-block:: @@ -377,7 +391,7 @@ Propriétés - :ref:`Catalog ` [] - .. line-block:: Liste des catalogues qui sera présenté à l'usager. - - + - - [] .. _igocatalogObject: @@ -416,15 +430,15 @@ Propriétés - Objet Catalog * - groupImpose - id*: String, title*: String - .. line-block:: - N.B: Propriété disponible sur un objet de type CompositeCatalog + N.B: Propriété disponible sur un objet de type CompositeCatalog Permet d'imposer l'utilisation d'un groupe à l'ensemble des couches appellées du catalogue. - id: Identifiant unique permettant de différencier les groupes entre eux. - title: Titre pour le groupe qui sera utilisé dans l'outil Catalog. - - - - + - + - * - matrixSet - String - .. line-block:: @@ -453,18 +467,18 @@ Propriétés - Objet Catalog * - queryParams - objet {} - .. line-block:: - Paramètres supplémentaires à ajouter à l'appel des - couches ajoutées à partir du service. - Que ce soit des paramètres normés (WMS|WMTS) + Paramètres supplémentaires à ajouter à l'appel des + couches ajoutées à partir du service. + Que ce soit des paramètres normés (WMS|WMTS) ou liés à votre service. - - + - - * - regFilters - String[] - .. line-block:: - Une liste d'expressions régulières (regex) - permettant de limiter les couches - d'information présentées dans l'outil + Une liste d'expressions régulières (regex) + permettant de limiter les couches + d'information présentées dans l'outil CatalogBrowser - - @@ -487,7 +501,7 @@ Propriétés - Objet Catalog - Boolean - .. line-block:: Permet d'affiché la légende sur le click du titre - des couches. + des couches. - true false - false * - sortDirection @@ -495,7 +509,7 @@ Propriétés - Objet Catalog - .. line-block:: Permet de trier l'ordre d'apparition des couches du catalogue dans l'outil CatalogBrowser - Influence l'ordre d'ajout des couches + Influence l'ordre d'ajout des couches d'informations à la carte. - asc desc - .. line-block:: @@ -519,7 +533,7 @@ Propriétés - Objet Catalog - String - .. line-block:: Pour les couches ajoutées, définit si le tooltip - (sulvol du titre) sera le résumé du "layer" + (sulvol du titre) sera le résumé du "layer" (**wms/wmts**) ou son titre - abstract title - title @@ -560,7 +574,7 @@ Propriétés - Objet CompositeCatalog (spécialisation de l'objet Catalog) .. list-table:: :widths: 10 10 30 15 10 :header-rows: 1 - + * - .. line-block:: Propriétés - .. line-block:: @@ -576,22 +590,22 @@ Propriétés - Objet CompositeCatalog (spécialisation de l'objet Catalog) - .. line-block:: Identifiant unique permettant de différencier les catalogues entre eux. - - - - + - + - * - **title*** - String - .. line-block:: Titre pour la source du catalogue qui sera utilisé dans l'outil Catalog. - - - - + - + - * - composite - :ref:`Catalog ` [] - .. line-block:: - Liste des catalogues utilisés dans un catalogue + Liste des catalogues utilisés dans un catalogue composé. - - - - + - + - Liens @@ -724,21 +738,21 @@ Propriétés - .. line-block:: Taille maximum du fichiers pouvant être lu par le fureteur. - .. line-block:: - + - 30 * - gpxAggregateInComment - Boolean - .. line-block:: Lorsque l'option est activée l'exportation du fichier vers le format GPX va rassembler les informations de l'enregistrement dans le champ «cmt» du gpx et assigner la valeur de l'ID au champ «name». - .. line-block:: - + - false * - forceNaming - Boolean - .. line-block:: Ajoute une boite texte au formulaire d'exportation qui permet de nommer le fichier exporter. - .. line-block:: - + - false * - formats - String[] @@ -756,6 +770,58 @@ Liens .. _igolanguage: + + +*************** +interactiveTour +*************** + + .. line-block:: + Tours intéractif de présentation de l'application + + +Exemples + + .. code:: json + + "interactiveTour": { + "tourInMobile": true, + "pathToConfigFile": "./config/interactiveTour.json" + }, + +Propriétés + + .. list-table:: + :widths: 10 10 30 15 10 + :header-rows: 1 + + * - .. line-block:: + Propriétés + - .. line-block:: + Type + - .. line-block:: + Description + - .. line-block:: + Valeurs possibles + - .. line-block:: + Valeur défaut + * - pathToConfigFile + - String + - .. line-block:: + Indique ou ce retrouve le fichier de configuartion des tours dans l'application. Voir la documentation plus loin sur les détails de la configurations des tours. + :ref:`Tour interactif configuration ` + - + - "./config/interactiveTour.json" + * - tourInMobile + - Boolean + - .. line-block:: + Indique si les tours interactifs sont aussi disponible en mode mobile. + - true/false + - + + Important : Les propriétés en caractère gras suivies d'un * sont obligatoires. + + *************** Language *************** @@ -1038,6 +1104,58 @@ Liens - `igo2-lib/packages/core/src/style/themes `_ + + +*************** +WelcomeWindow +*************** + + .. line-block:: + Affiche une fenêtre accueil à l'entrée dans l'application. + NB. : Pour une application sans authentification, simplement ne pas mettre ces configurations. + +Exemples + + .. code:: json + + "welcomeWindow": { + "showAgainOnNewIGOVersion": true, + "nbVisitToShowAgain": 30 + } + +Propriétés + + .. list-table:: + :widths: 10 10 30 15 10 + :header-rows: 1 + + * - .. line-block:: + Propriétés + - .. line-block:: + Type + - .. line-block:: + Description + - .. line-block:: + Valeurs possibles + - .. line-block:: + Valeur défaut + * - nbVisitToShowAgain + - Number + - .. line-block:: + Lorsque l'utilisateur coche la case ne plus afficher, la fenêtre d'accueil reviendra après le nombre de visite indiqué dans ce paramètre. + - + - 30 + * - showAgainOnNewIGOVersion + - Boolean + - .. line-block:: + Lorsque l'utilisateur coche la case ne plus afficher, la fenêtre d'accueil reviendra si la version IGO est différente de la version lors de sa visite précédente. + - + - true + + Important : Les propriétés en caractère gras suivies d'un * sont obligatoires. + + + *************** Exemple complet *************** @@ -1070,10 +1188,10 @@ Exemple, dans une application cartographique vous pouvez avoir plusieurs context On peut y définir: - l'étendue cartographique - les couches d'informations disponibles - - les outils accessibles + - les outils accessibles - certaines configurations d'outils -Quant à lui, le fichier **base.json** contient les éléments +Quant à lui, le fichier **base.json** contient les éléments partagés entre chancun des contextes l'héritant. Selon l'exemple précédent, dans une application cartographique, vous avez 3 contextes (thématiques): @@ -1082,9 +1200,9 @@ Selon l'exemple précédent, dans une application cartographique, vous avez 3 co - routes.json - risques.json -Plutôt que de répéter 3 fois les mêmes éléments -(fonds cartographiques, outils, couches de base) dans chaque contexte, -il est possibe de déclarer dans le **base.json** les éléments communs +Plutôt que de répéter 3 fois les mêmes éléments +(fonds cartographiques, outils, couches de base) dans chaque contexte, +il est possibe de déclarer dans le **base.json** les éléments communs aux 3 contextes. La maintenance de l'application en sera facilitée. @@ -1096,7 +1214,7 @@ Important : Notez que le fichier nom_du_contexte.json a préséance sur le fichi *************************** Résumé fichier de contexte -*************************** +*************************** .. list-table:: :widths: 10 10 30 15 @@ -1119,14 +1237,14 @@ Résumé fichier de contexte Config d'outils ... * - .. line-block:: - **layers*** + **layers*** - :ref:`layer[] ` - .. line-block:: Liste des couches d'informations disponibles pour - le contexte - sélectionné. + le contexte + sélectionné. - .. line-block:: Map MapDetails @@ -1136,17 +1254,17 @@ Résumé fichier de contexte Définition de la carte lors de l'ouverture initial du contexte - - + - * - message - `Message`_ - .. line-block:: Présentation d'un message a l'ouverture du contexte. - - + - * - `toolbar`_ - String[] - .. line-block:: Liste des outils - disponibles dans + disponibles dans l'application. L'ordre dans la liste correspond @@ -1157,7 +1275,7 @@ Résumé fichier de contexte * - `tools`_ - Objet[] - .. line-block:: - Liste des configurations + Liste des configurations des outils présentes dans l'application. - Tous @@ -1249,7 +1367,7 @@ Exemples Propriétés .. line-block:: - Permet de définir une liste de couches. Référez-vous à la description de ce qu'est un :ref:`layer `. + Permet de définir une liste de couches. Référez-vous à la description de ce qu'est un :ref:`layer `. Liens @@ -1318,7 +1436,7 @@ Propriétés .. list-table:: :widths: 10 10 30 15 10 :header-rows: 1 - + * - .. line-block:: Propriétés - .. line-block:: @@ -1333,43 +1451,43 @@ Propriétés - String - Le format du message html ou text. Selon le choix, une deuxième configuration devra être définie soit html ou text. - 'text', 'html' - - + - * - html - String - - Le html du message sur une seule ligne. Cette configuration est obligatoire si le format = 'html. - - - - + - Le html du message sur une seule ligne. Cette configuration est obligatoire si le format = 'html. + - + - * - icon - String - - Icone à ajouter au message. - - - - + - Icone à ajouter au message. + - + - * - options.template - Sting - En construction - - - - + - + - * - options.timeOut - Number - Temps avant la disparition du message, en miliseconde. - - - - + - + - * - text - Sting - Le text du message à afficher. Cette configuration remplace la configuration html. - - - - + - + - * - title - Sting - Le titre du message à afficher. Cette configuration s'active seulement avec la configuration text et ne sera pas pris en compte avec la configuration html. - - - - + - + - * - type - Sting - Le type du message à afficher. Avertissement, erreur ou information. Selon le type choisi une couleur spécifiée sera appliquée selon la thématique de couleur de l'application. - 'alert', 'error', 'info', 'success' - - - + - + Important : Les propriétés en caractère gras suivis d'un * sont obligatoires. @@ -1443,14 +1561,14 @@ Tools Définit la liste des configurations permises pour chaque outil. Pour les options spécifiques à chaque outil, veuillez vous référer aux descriptif de l'outil. Cette section détaille seulement les propriétés communes. - + Exemples .. code:: json { - "icon" : "iconName", + "icon" : "iconName", "name" : "catalogBrowser", "title": "TitreOutilQuiDoitEtreTraduit", "options" : { @@ -1504,7 +1622,7 @@ Propriétés - String - .. line-block:: Le titre affiché dans l'application. Sujet aux traductions. - Si vous modifiez le titre par défaut, vous devez ajouter + Si vous modifiez le titre par défaut, vous devez ajouter ce titre dans les langues supportées par IGO2 (fr-en). - fichiers dans `Language`_ - diff --git a/docs/index.rst b/docs/index.rst index 8f3f022fb..d423be35f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ .. include:: global.rst - + .. meta:: :DC.creator: Gouvernement du Québec :DC.language: fr @@ -7,15 +7,15 @@ ----------------------- IGO-2 ----------------------- - -Voici le site de documentation pour la IGO2 : Infrastructure Géomatique Ouverte 2.0 (|igo2|_) / Open GIS Infrastructure project version 2.0. - + +Voici le site de documentation pour la IGO2 : Infrastructure Géomatique Ouverte 2.0 (|igo2|_) / Open GIS Infrastructure project version 2.0. + |igo-logo| * Installation : `https://github.com/infra-geo-ouverte/igo2#installation-et-démarrage `_ * Démo : ``_ -* Dépôt GitHub : +* Dépôt GitHub : - ``_ - ``_ @@ -30,13 +30,14 @@ Dernière mise à jour de la documentation : |date| (|time| UTC) .. toctree:: :maxdepth: 5 - + english introduction - config_json + config_json properties url_control + interactiveTour_config shortcut - + .. note:: Cette documentation est en construction. diff --git a/docs/interactiveTour_config.rst b/docs/interactiveTour_config.rst new file mode 100644 index 000000000..9da1c69f4 --- /dev/null +++ b/docs/interactiveTour_config.rst @@ -0,0 +1,438 @@ + +.. _interactiveTourRef: + +****************************** +Tour interactif configuration +****************************** + +Sommaire +=============== + +Il est possible de configurer des tours interactifs pour présenter le fonctionnement de l'application et de ces outils. +La librairie utilisée par IGO pour ce faire est Shepherdjs (https://shepherdjs.dev). Plusieurs tours de présentation sont possibles, +un tour global, général et des particuliers pour chacun des outils. Chaque tour à sa propre configuration. Les tours sont constitués de +plusieurs étapes, communément appelé des 'steps' ou étapes. Chaque 'step' met en surbrillance un élément de l'application et affiche +un message de description de cet élément. Le pilote peut ainsi configurer plusieurs 'step' à chacun de ces tours interactifs. +En plus de sélectionner des éléments à mettre en surbrillance, le pilote peut aussi configurer certaines actions lors du tour. + + + +Configurer les tours +--------------------- + +Les tours sont définis dans le fichier interactiveTour.json déposé dans le dossier config de l'application. + +Chaque tour possède des options de configuration qui s'appliqueront à ce tour et/ou s'appliqueront à chaque step de ce tour. +À l'intérieur du fichier, chaque tour doit avoir la syntaxe suivante: global : {...} ou nomGénériqueDeOutil: {...} +pour les tours sur les outils. Voir documentation Tools pour la liste de nom générique des outils (`IGO doc `_) +Lorsqu'une certaine configuration est détectée par l'application, le bouton relié apparait automatiquement. Par exemple, lorsque vous aurez +configuré un tour pour un outil X, le bouton de présentation apparaitra dans l'entête de l'outil. + +Une configuration pour ne pas avoir de tour interactif en mode mobile est aussi disponible dans le fichier config.json: +"interactiveTourInMobile": true +par défaut les tours interactifs seront présents en mode mobile. + + +Exemples + + .. code:: json + + { + "global": { + "title": "interactiveTour.global.title", + "position": "auto", + "steps": [ + { + "element": ".menu-button", + "title": "interactiveTour.global.menu-button-title", + "text": "interactiveTour.global.menu-button", + "position": "right", + }, + { + "element": ".igo-search-bar-container", + "text": "interactiveTour.global.search-bar", + "noBackButton": true, + }, + { + "element": "igo-toolbox", + "title": "interactiveTour.global.toolbox-title", + "text": "interactiveTour.global.toolbox", + "beforeShow": { + "element": "#homeButton", + "action": "click" + } + } + ] + } + } + +Propriétés - Objet InteractiveTourOptions + + .. list-table:: + :widths: 10 10 30 15 10 + :header-rows: 1 + + * - .. line-block:: + Propriétés + - .. line-block:: + Type + - .. line-block:: + Description + - .. line-block:: + Valeurs possibles + - .. line-block:: + Valeur défaut + * - class + - String + - .. line-block:: + Définit la classe à appliquer aux boites contenant les messages d'aide + - + - + * - disableInteraction + - Boolean + - .. line-block:: + Permet ou non à l'utilisateur de cliquer sur les éléments en surbrillance + - true | false + - true + * - highlightClass + - Boolean + - .. line-block:: + Définit la classe à appliquer aux éléments en surbrillance + - + - + * - position + - String + - .. line-block:: + Définit la position des boites aide + - 'auto', 'right', 'left', 'bottom', 'top'. NB.: Si la propriété position n'est pas présente, les boites seront disposées au centre de l'écran. + - + * - scrollToElement + - Boolean + - .. line-block:: + Indique si on défile la page sur l'élément en surbrillance + - true | false + - + * - steps + - :ref:`InteractiveTourStep ` + - .. line-block:: + Une liste de step + - + - + * - title + - String + - .. line-block:: + Le titre de toutes les boites aide + - ... + - ... + + Important : Les propriétés en caractère gras suivies d'un * sont obligatoires. + + +Liens + - `TourOptions interface `_ + + +Configurer les 'steps' des tours +-------------------------------- + +Chaque 'step' est constitué d'au minimum 2 éléments. +D'abord "element" correspond à l'élément HTML qui doit être mis en surbrillance. On peut indiquer un nom ID, une CLASS ou autre élément HTML +qui peut être retrouvé via les fonctions de document HTML: document.getElementsByTagName(), document.getElementsByClassName(), +document.querySelector(), document.getElementById(). +Pour voir vos éléments html vous pouvez utiliser l'inspecteur de votre navigateur internet (clic droit sur l'élément -> inspecter) + +Le second élément obligatoire est "text" ou l'on saisit le message inscrit dans de la boîte. Du HTML peut y être inséré. + +NB.: attention à la séquence que prendra votre tour, l'élément doit être visible au moment où le step est déclenché pour être +mis en surbrillance sinon votre tour pourrait avoir certains problèmes et/ou vous devrez ajouter des actions pour attendre que l'élément HTML +soit visible. + + + +Steps, autres propriétés +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +En plus des 2 propriétés essentielles à chaque step, il est possible d'en ajouter d'autre comme "title" ou "position" pour mettre un titre +à la boite d'aide et indiquer la position de la boite. Il est aussi possible de ne pas permettre les clics par l'utilisateur dans ce step à +l'aide de "disableInteraction": true ou de ne pas mettre le bouton précédent dans un step particulier à l'aide de la propriété "noBackButton". + + + + +Steps, actions et déclenchements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Il est aussi possible de réaliser des actions lors d'un step. Pour ce faire simplement ajouter le moment ou doit être déclenché l'action +dans le step et indiquer l'action voulu. + + +Possibilité de déclenchement: + * beforeShow: Déclenchement avant l'apparition de la boite. Attends avant d'ouvrir l'étape (en combinaison de waitFor qui prend du css) + * beforeChange: Déclenchement avant le changement de boite. Attends avant de passer à l'étape suivante (en combinaison de waitFor qui prend du css) + * onShow: Déclenchement lors de l'apparition de la boite + * onHide: Déclenchement lorsque la boite disparait + + +Actions possibles: + * click : Cliquer sur l'élément + +Options des actions: + * waitFor: Indiquer l'élement html à attendre avant de fare l'action + * maxWait: Temps à attendre avant l'abandon + * condition: Condition à respecter pour effectuer l'action + * element: Élement à cliquer sur + + +Exemples + + .. code:: json + + { + "global": { + "steps": [ + { + "element": ".menu-button", + "text": "Un step avec ces options", + "title": "

titre de la boite

", + "position": "bottom", + "disableInteraction": true + }, + { + "element": ".menu-button", + "text": "Effectue un clic sur le bouton menu à l'arrivée de cette boite d'aide", + "onShow": { + "action": "click" + } + }, + { + "element": ".igo-search-bar-container", + "text": "Effectue un clic sur le bouton menu à la fermeture de la boite d'aide de la recherche", + "onHide": { + "element": ".menu-button", + "action": "click" + } + }, + { + "element": ".menu-button", + "text": "Voici le menu " + }, + { + "element": ".menu-button", + "text": "Effectue un clic à l'arrivée de la boite seulement si le menu est fermé", + "onShow": { + "action": "click", + "condition": "app-sidenav:not([ng-reflect-opened=true])" + } + }, + { + "element": ".menu-button", + "text": "Voici le menu" + }, + { + "element": "igo-actionbar-item:nth-child(2) mat-list-item", + "text": "clic sur l'outil context", + "beforeShow": { + "action": "click" + } + }, + { + "element": "igo-actionbar-item:nth-child(2) mat-list-item", + "text": "clic sur l'outil context mais avant que la boite apparaisse clic sur le conteneur d'outil et avant l'apparition de la boite, clic sur le bouton home", + "beforeShow": { + "element": "#homeButton", + "action": "click" + }, + "beforeChange": { + "action": "click", + "waitFor": ".igo-tool-container" + } + }, + { + "element": "igo-context-item:nth-of-type(3)", + "text": "clic sur le 3e context mais avant de cliquer attend que l'élément igo-list soit arrivé", + "beforeChange": { + "action": "click", + "waitFor": "igo-list" + } + } + ] + } + } + + + +.. _igoInteractiveTourStep: + +Propriétés - Objet InteractiveTourStep + + .. list-table:: + :widths: 10 10 30 15 10 + :header-rows: 1 + + * - .. line-block:: + Propriétés + - .. line-block:: + Type + - .. line-block:: + Description + - .. line-block:: + Valeurs possibles + - .. line-block:: + Valeur défaut + * - beforeChange + - InteractiveTourAction + - .. line-block:: + Déclenchement avant le changement de boite. Attends avant de passer à l'étape suivante (en combinaison de waitFor qui prend du css) + - + - + * - beforeShow + - InteractiveTourAction + - .. line-block:: + Déclenchement avant l'apparition de la boite. Attends avant d'ouvrir l'étape (en combinaison de waitFor qui prend du css) + - + - + * - class + - String + - .. line-block:: + Définit la classe à appliquer aux boites contenant les messages d'aide + - + - + * - disableInteraction + - Boolean + - .. line-block:: + Permet ou non à l'utilisateur de cliquer sur l'éléments du step en surbrillance + - true | false + - true + * - element + - string + - .. line-block:: + Elément HTML à mettre en surbrillance. NB.: doit être visible lors du déclanchement + - + - + * - highlightClass + - Boolean + - .. line-block:: + Définit la classe à appliquer aux éléments en surbrillance + - + - + * - noBackButton + - Boolean + - .. line-block:: + Définit si le step aura un bouton précédent + - + - + * - onHide + - InteractiveTourAction + - .. line-block:: + Déclenchement lorsque la boite disparait + - + - + * - onShow + - InteractiveTourAction + - .. line-block:: + Déclenchement lors de l'apparition de la boite + - + - + * - position + - String + - .. line-block:: + Définit la position des boites aide + - 'auto', 'right', 'left', 'bottom', 'top'. NB.: Si la propriété position n'est pas présente, les boites seront disposées au centre de l'écran. + - + * - scrollToElement + - Boolean + - .. line-block:: + Indique si on défile la page sur l'élément en surbrillance + - true | false + - + * - text + - String + - .. line-block:: + Le texte inscrit dans la boite d'aide. On peut y mettre du html. NB.: voir traduction + - + - + * - title + - String + - .. line-block:: + Le titre de toutes les boites aide + - ... + - ... + + Important : Les propriétés en caractère gras suivies d'un * sont obligatoires. + + +Liens + - `InteractiveTourStep interface `_ + + + +Traduction +^^^^^^^^^^^^^^^^^^^^^^^^^^ +Il est possible de mettre une traduction aux différents messages, pour ce faire vous devez utiliser une clé de traduction que vous définissez + et inscrire le message dans les fichiers en.json et fr.json. Le message s'affichera en fonction de la langue de votre navigateur internet. + + + +Exemple + +interactiveTour.json + + .. code:: json + + { + "global": { + "steps": [ + { + "element": ".igo-search-bar-container", + "title": "interactiveTour.global.maCleDeTraduction_titre", + "text": "interactiveTour.global.maCleDeTraduction" + }, + ] + } + } + +en.json + + .. code:: json + + { + "interactiveTour": { + "global": { + "maCleDeTraduction_titre": "Nice interatif tour", + "maCleDeTraduction": "This is the search bar " + } + +fr.json + + .. code:: json + + { + "interactiveTour": { + "global": { + "maCleDeTraduction_titre": "Super tour intératif", + "maCleDeTraduction": "Voici la barre de recherche " + } + + +Dépannage +----------- + +Je ne vois pas le bouton de mon tour apparaitre. + Solution: + - Vérifier que le fichier interactiveTour.json est bien présent dans le dossier config de votre application. + - Vérifier que le nom de l'outil est bien exact + - Vérifier que la syntaxe du tour est bien présentée de cette façon: global: {...} ou nomGénériqueDeOutil:{...} + - Si vous êtes en mode mobile vérifier la configuration dans le fichier config.json: "introInteractiveTourInMobile": true + +L'élément de mon tour n'est pas mis en surbrillance. + Solution: + - Vérifier que votre élément est bien sélectionnable via la console et document.querySelector('monElement') + - Vérifier selon la séquence si votre élément est bien disponible lors du déclanchement du step. Il se pourrait que vous deviez ajouter + une action ainsi qu'un wait sur votre élément HTML si par exemple vous cliquez sur un menu et voulez sélectionner un élément à l'intérieur + dans l'étape suivante. + + + +Liens + + - `Exemple de configuration `_ + - `component igo2-lib/packages/common/src/lib/interactive-tour `_ diff --git a/package-lock.json b/package-lock.json index 48fd10509..f54c614c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2219,6 +2219,11 @@ } } }, + "@popperjs/core": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.4.tgz", + "integrity": "sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==" + }, "@rollup/plugin-commonjs": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-13.0.0.tgz", @@ -3051,6 +3056,15 @@ } } }, + "angular-shepherd": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/angular-shepherd/-/angular-shepherd-0.7.0.tgz", + "integrity": "sha512-pM9ibVJumxQ9tIzkhnr1KpbJJKLaXpofotxn5H/3tVkO5RtijSQ3Y3fZDF864R4SXUnW/wND1pmnRaS5KUpTdQ==", + "requires": { + "shepherd.js": "^8.0.1", + "tslib": "^2.0.0" + } + }, "angular2-notifications": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/angular2-notifications/-/angular2-notifications-2.0.0.tgz", @@ -6126,8 +6140,7 @@ "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" }, "default-compare": { "version": "1.0.0", @@ -20935,6 +20948,16 @@ "rechoir": "^0.6.2" } }, + "shepherd.js": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/shepherd.js/-/shepherd.js-8.0.2.tgz", + "integrity": "sha512-qFt5vrnVoshg6fS5gKGQznYGyIaEclyKKSl1Siw6gO7GyKnCEJ2aBDrp8pxKusMfZo5J5dtgeKJtsRav5cimjA==", + "requires": { + "@popperjs/core": "^2.4.4", + "deepmerge": "^4.2.2", + "smoothscroll-polyfill": "^0.4.4" + } + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -20982,6 +21005,11 @@ "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", "dev": true }, + "smoothscroll-polyfill": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz", + "integrity": "sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg==" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/package.json b/package.json index 3872d3c43..4ca049003 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@igo2/utils": "^1.4.2", "@mat-datetimepicker/core": "^4.1.0", "@turf/point-on-feature": "^5.1.5", + "angular-shepherd": "^0.7.0", "bowser": "^2.9.0", "classlist.js": "^1.1.20150312", "core-js": "^3.6.5", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e5801f9c0..42a4d8b45 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -83,7 +83,7 @@ export class AppComponent { message })) ) - .subscribe(rep => + .subscribe((rep) => this.messageService.alert(rep.message, rep.title, { timeOut: 15000 }) diff --git a/src/app/pages/portal/portal.component.scss b/src/app/pages/portal/portal.component.scss index d12f7e14c..5bea71086 100644 --- a/src/app/pages/portal/portal.component.scss +++ b/src/app/pages/portal/portal.component.scss @@ -1,5 +1,18 @@ @import './portal.variables'; +#tour-button { + font-weight: bold; + position: absolute; + left: $igo-margin; + top: 50px; + z-index: 4; +} +.tour-button-title { + @include mobile { + display: none; + } +} + /*** Main ***/ :host { width: 100%; @@ -84,11 +97,19 @@ igo-map-browser.expansion-offset ::ng-deep .ol-overlaycontainer-stopevent { bottom: 279px; } -igo-map-browser.toast-offset-scale-line ::ng-deep .ol-overlaycontainer-stopevent ::ng-deep .ol-scale-line { +igo-map-browser.toast-offset-scale-line + ::ng-deep + .ol-overlaycontainer-stopevent + ::ng-deep + .ol-scale-line { bottom: 50px; } -igo-map-browser.toast-offset-attribution ::ng-deep .ol-overlaycontainer-stopevent ::ng-deep .ol-attribution { +igo-map-browser.toast-offset-attribution + ::ng-deep + .ol-overlaycontainer-stopevent + ::ng-deep + .ol-attribution { @include mobile { bottom: 50px; } @@ -135,8 +156,8 @@ app-expansion-panel { z-index: 5; @include mobile { - @supports (-webkit-appearance:none) and (not (overflow:-webkit-marquee)) - and (not (-ms-ime-align:auto)) and (not (-moz-appearance:none)) { + @supports (-webkit-appearance: none) and (not (overflow: -webkit-marquee)) + and (not (-ms-ime-align: auto)) and (not (-moz-appearance: none)) { z-index: 1; } } diff --git a/src/app/pages/portal/portal.component.ts b/src/app/pages/portal/portal.component.ts index 0191af020..4c1d15793 100644 --- a/src/app/pages/portal/portal.component.ts +++ b/src/app/pages/portal/portal.component.ts @@ -9,7 +9,7 @@ import { import { ActivatedRoute, Params } from '@angular/router'; import { Subscription, of, BehaviorSubject } from 'rxjs'; import { debounceTime, take } from 'rxjs/operators'; - +import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { MapBrowserPointerEvent as OlMapBrowserPointerEvent } from 'ol/MapBrowserEvent'; import * as olProj from 'ol/proj'; @@ -80,6 +80,9 @@ import { } from './portal.animation'; import { HttpClient } from '@angular/common/http'; +import { WelcomeWindowComponent } from './welcome-window/welcome-window.component'; +import { WelcomeWindowService } from './welcome-window/welcome-window.service'; + @Component({ selector: 'app-portal', templateUrl: './portal.component.html', @@ -137,8 +140,10 @@ export class PortalComponent implements OnInit, OnDestroy { ] }; - @ViewChild('mapBrowser', { read: ElementRef, static: true }) mapBrowser: ElementRef; - @ViewChild('searchBar', { read: ElementRef, static: true }) searchBar: ElementRef; + @ViewChild('mapBrowser', { read: ElementRef, static: true }) + mapBrowser: ElementRef; + @ViewChild('searchBar', { read: ElementRef, static: true }) + searchBar: ElementRef; get map(): IgoMap { return this.mapState.map; @@ -256,12 +261,13 @@ export class PortalComponent implements OnInit, OnDestroy { private queryState: QueryState, private toolState: ToolState, private searchSourceService: SearchSourceService, - private searchService: SearchService, private configService: ConfigService, private importService: ImportService, private http: HttpClient, private languageService: LanguageService, - private messageService: MessageService + private messageService: MessageService, + private welcomeWindowService: WelcomeWindowService, + public dialogWindow: MatDialog ) { this.hasExpansionPanel = this.configService.getConfig('hasExpansionPanel'); this.forceCoordsNA = this.configService.getConfig('app.forceCoordsNA'); @@ -273,9 +279,11 @@ export class PortalComponent implements OnInit, OnDestroy { ngOnInit() { window['IGO'] = this; - this.authService.authenticate$.subscribe( - () => (this.contextLoaded = false) - ); + this.initWelcomeWindow(); + + this.authService.authenticate$.subscribe((authenticated) => { + this.contextLoaded = false; + }); this.context$$ = this.contextState.context$.subscribe( (context: DetailedContext) => this.onChangeContext(context) @@ -307,7 +315,7 @@ export class PortalComponent implements OnInit, OnDestroy { { id: '5', name: 'Name 5', description: 'Description 5' } ]); - this.queryStore.count$.subscribe(i => { + this.queryStore.count$.subscribe((i) => { this.map.viewController.padding[2] = i ? 280 : 0; }); this.readQueryParams(); @@ -343,7 +351,7 @@ export class PortalComponent implements OnInit, OnDestroy { const results = event.features.map((feature: Feature) => { let querySearchSource = querySearchSourceArray.find( - s => s.title === feature.meta.sourceTitle + (s) => s.title === feature.meta.sourceTitle ); if (!querySearchSource) { querySearchSource = new QuerySearchSource({ @@ -430,7 +438,7 @@ export class PortalComponent implements OnInit, OnDestroy { return; } - this.route.queryParams.pipe(debounceTime(250)).subscribe(params => { + this.route.queryParams.pipe(debounceTime(250)).subscribe((params) => { if (!params['context'] || params['context'] === context.uri) { this.readLayersQueryParams(params); } @@ -481,8 +489,8 @@ export class PortalComponent implements OnInit, OnDestroy { this.map.overlay.removeFeatures( this.searchStore .all() - .filter(f => f.meta.dataType === FEATURE) - .map(f => f.data as Feature) + .filter((f) => f.meta.dataType === FEATURE) + .map((f) => f.data as Feature) ); } @@ -526,7 +534,7 @@ export class PortalComponent implements OnInit, OnDestroy { } searchCoordinate(coord: [number, number]) { - this.searchBarTerm = coord.map(c => c.toFixed(6)).join(', '); + this.searchBarTerm = coord.map((c) => c.toFixed(6)).join(', '); } updateMapBrowserClass(e) { @@ -679,7 +687,7 @@ export class PortalComponent implements OnInit, OnDestroy { } private readQueryParams() { - this.route.queryParams.pipe(debounceTime(250)).subscribe(params => { + this.route.queryParams.pipe(debounceTime(250)).subscribe((params) => { this.readToolParams(params); this.readSearchParams(params); this.readFocusFirst(params); @@ -700,11 +708,8 @@ export class PortalComponent implements OnInit, OnDestroy { private readFocusFirst(params: Params) { if (params['sf'] === '1' && this.termDefinedInUrl) { const entities$$ = this.searchStore.entities$ - .pipe( - debounceTime(500), - take(1) - ) - .subscribe(entities => { + .pipe(debounceTime(500), take(1)) + .subscribe((entities) => { entities$$.unsubscribe(); if (entities.length) { this.computeFocusFirst(); @@ -740,13 +745,15 @@ export class PortalComponent implements OnInit, OnDestroy { private readLayersQueryParamsWMS(params: Params) { if ((params['layers'] || params['wmsLayers']) && params['wmsUrl']) { - const nameParamLayers = (params['wmsLayers']) ? 'wmsLayers' : 'layers'; // for maintain compatibility + const nameParamLayers = params['wmsLayers'] ? 'wmsLayers' : 'layers'; // for maintain compatibility const layersByService = params[nameParamLayers].split('),('); const urls = params['wmsUrl'].split(','); let cnt = 0; - urls.forEach(url => { - const currentLayersByService = this.extractLayersByService(layersByService[cnt]); - currentLayersByService.forEach(layer => { + urls.forEach((url) => { + const currentLayersByService = this.extractLayersByService( + layersByService[cnt] + ); + currentLayersByService.forEach((layer) => { const layerFromUrl = layer.split(':igoz'); const layerOptions = { url: url, @@ -773,9 +780,11 @@ export class PortalComponent implements OnInit, OnDestroy { const layersByService = params['wmtsLayers'].split('),('); const urls = params['wmtsUrl'].split(','); let cnt = 0; - urls.forEach(url => { - const currentLayersByService = this.extractLayersByService(layersByService[cnt]); - currentLayersByService.forEach(layer => { + urls.forEach((url) => { + const currentLayersByService = this.extractLayersByService( + layersByService[cnt] + ); + currentLayersByService.forEach((layer) => { const layerFromUrl = layer.split(':igoz'); const layerOptions = { url: url, @@ -803,15 +812,16 @@ export class PortalComponent implements OnInit, OnDestroy { const lastIndex = url.lastIndexOf('/'); const fileName = url.slice(lastIndex + 1, url.length); - this.http.get(`${url}`, {responseType: 'blob'}). - subscribe((data) => { - const file = new File([data], fileName, {type: data.type, lastModified: Date.now()}); - this.importService.import(file). - subscribe( - (features: Feature[]) => this.onFileImportSuccess(file, features), - (error: Error) => this.onFileImportError(file, error) - ); + this.http.get(`${url}`, { responseType: 'blob' }).subscribe((data) => { + const file = new File([data], fileName, { + type: data.type, + lastModified: Date.now() }); + this.importService.import(file).subscribe( + (features: Feature[]) => this.onFileImportSuccess(file, features), + (error: Error) => this.onFileImportError(file, error) + ); + }); } } @@ -869,7 +879,7 @@ export class PortalComponent implements OnInit, OnDestroy { } } }) - .subscribe(l => { + .subscribe((l) => { this.map.addLayer(l); }) ); @@ -899,7 +909,7 @@ export class PortalComponent implements OnInit, OnDestroy { layer: name } } as any) - .subscribe(l => { + .subscribe((l) => { this.map.addLayer(l); }) ); @@ -945,4 +955,32 @@ export class PortalComponent implements OnInit, OnDestroy { } return visible; } + + private initWelcomeWindow(): void { + const authConfig = this.configService.getConfig('auth'); + if (authConfig) { + this.authService.logged$.subscribe((logged) => { + if (logged) { + this.createWelcomeWindow(); + } + }); + } else { + this.createWelcomeWindow(); + } + } + + private createWelcomeWindow(): void { + if (this.welcomeWindowService.hasWelcomeWindow()) { + const welcomWindowConfig: MatDialogConfig = this.welcomeWindowService.getConfig(); + + const dialogRef = this.dialogWindow.open( + WelcomeWindowComponent, + welcomWindowConfig + ); + + dialogRef.afterClosed().subscribe((result) => { + this.welcomeWindowService.afterClosedWelcomeWindow(); + }); + } + } } diff --git a/src/app/pages/portal/portal.module.ts b/src/app/pages/portal/portal.module.ts index baa4a1620..a037644d8 100644 --- a/src/app/pages/portal/portal.module.ts +++ b/src/app/pages/portal/portal.module.ts @@ -1,9 +1,11 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; + import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatDialogModule } from '@angular/material/dialog'; import { IgoCoreModule } from '@igo2/core'; import { @@ -15,7 +17,8 @@ import { IgoFlexibleModule, IgoContextMenuModule, IgoToolModule, - IgoEntityTableModule + IgoEntityTableModule, + IgoInteractiveTourModule } from '@igo2/common'; import { @@ -39,6 +42,8 @@ import { AppToastPanelModule } from './toast-panel/toast-panel.module'; import { AppSidenavModule } from './sidenav/sidenav.module'; import { PortalComponent } from './portal.component'; +import { WelcomeWindowComponent } from './welcome-window/welcome-window.component'; +import { IgoWelcomeWindowModule } from './welcome-window/welcome-window.module'; @NgModule({ imports: [ @@ -47,6 +52,7 @@ import { PortalComponent } from './portal.component'; MatButtonModule, MatIconModule, MatSidenavModule, + MatDialogModule, IgoCoreModule, IgoFeatureModule, IgoImportExportModule, @@ -69,9 +75,12 @@ import { PortalComponent } from './portal.component'; MapOverlayModule, IgoContextManagerModule, IgoContextMapButtonModule, - IgoEntityTableModule + IgoEntityTableModule, + IgoInteractiveTourModule, + IgoWelcomeWindowModule ], exports: [PortalComponent], - declarations: [PortalComponent] + declarations: [PortalComponent], + entryComponents: [WelcomeWindowComponent] }) export class PortalModule {} diff --git a/src/app/pages/portal/portal.theming.scss b/src/app/pages/portal/portal.theming.scss index 0003eb006..b36f76058 100644 --- a/src/app/pages/portal/portal.theming.scss +++ b/src/app/pages/portal/portal.theming.scss @@ -1,5 +1,7 @@ @import './sidenav/sidenav.theming'; +@import './welcome-window/welcome-window.theming'; @mixin app-portal-theming($theme) { @include app-sidenav-theming($theme); + @include igo-welcome-window-theming($theme); } diff --git a/src/app/pages/portal/sidenav/sidenav.component.html b/src/app/pages/portal/sidenav/sidenav.component.html index 401c0025c..b62fbbebe 100644 --- a/src/app/pages/portal/sidenav/sidenav.component.html +++ b/src/app/pages/portal/sidenav/sidenav.component.html @@ -28,6 +28,11 @@ + + + diff --git a/src/app/pages/portal/sidenav/sidenav.module.ts b/src/app/pages/portal/sidenav/sidenav.module.ts index e6052080c..458e09f3b 100644 --- a/src/app/pages/portal/sidenav/sidenav.module.ts +++ b/src/app/pages/portal/sidenav/sidenav.module.ts @@ -6,7 +6,12 @@ import { MatSidenavModule } from '@angular/material/sidenav'; import { MatTooltipModule } from '@angular/material/tooltip'; import { IgoLanguageModule } from '@igo2/core'; -import { IgoPanelModule, IgoFlexibleModule, IgoToolModule } from '@igo2/common'; +import { + IgoPanelModule, + IgoFlexibleModule, + IgoToolModule, + IgoInteractiveTourModule +} from '@igo2/common'; import { IgoFeatureModule } from '@igo2/geo'; import { IgoContextManagerModule } from '@igo2/context'; @@ -24,7 +29,8 @@ import { SidenavComponent } from './sidenav.component'; IgoFlexibleModule, IgoContextManagerModule, IgoToolModule, - IgoFeatureModule + IgoFeatureModule, + IgoInteractiveTourModule ], exports: [SidenavComponent], declarations: [SidenavComponent] diff --git a/src/app/pages/portal/toast-panel/toast-panel.component.html b/src/app/pages/portal/toast-panel/toast-panel.component.html index 2971e5495..eb3e57187 100644 --- a/src/app/pages/portal/toast-panel/toast-panel.component.html +++ b/src/app/pages/portal/toast-panel/toast-panel.component.html @@ -6,6 +6,7 @@ (click)="onToggleClick($event)"> + + +

{{'welcomeWindow.title' | translate}}

+ + + +
+ +
+ + +
+ +

+
+ + + + +
+ + diff --git a/src/app/pages/portal/welcome-window/welcome-window.component.scss b/src/app/pages/portal/welcome-window/welcome-window.component.scss new file mode 100644 index 000000000..88d3f96f5 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.component.scss @@ -0,0 +1,12 @@ +.spacer { + flex: 1 1 auto; +} + +.button-container { + display: flex; + justify-content: space-around; +} + +#doNotShowCheck { + margin-right: 5px; +} diff --git a/src/app/pages/portal/welcome-window/welcome-window.component.spec.ts b/src/app/pages/portal/welcome-window/welcome-window.component.spec.ts new file mode 100644 index 000000000..a6f95ca26 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WelcomeWindowComponent } from './welcome-window.component'; + +describe('WelcomeWindowComponent', () => { + let component: WelcomeWindowComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ WelcomeWindowComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WelcomeWindowComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/portal/welcome-window/welcome-window.component.ts b/src/app/pages/portal/welcome-window/welcome-window.component.ts new file mode 100644 index 000000000..e09b76834 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.component.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { WelcomeWindowService } from './welcome-window.service'; + +@Component({ + selector: 'app-welcome-window', + templateUrl: './welcome-window.component.html', + styleUrls: ['./welcome-window.component.scss'] +}) +export class WelcomeWindowComponent { + // isVisible = true; + showAgain = false; + + constructor( + public dialog: MatDialog, + private welcomeWindowService: WelcomeWindowService + ) {} + + closeWelcomeWindow() { + this.dialog.closeAll(); + } + + get html() { + const _html = 'welcomeWindow.html'; + return _html; + } + + setShowAgain() { + this.welcomeWindowService.showAgain = this.showAgain; + } +} diff --git a/src/app/pages/portal/welcome-window/welcome-window.module.ts b/src/app/pages/portal/welcome-window/welcome-window.module.ts new file mode 100644 index 000000000..98df12f48 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatDialogModule } from '@angular/material/dialog'; + +import { WelcomeWindowComponent } from './welcome-window.component'; +import { IgoLanguageModule } from '@igo2/core'; +import { IgoInteractiveTourModule, IgoCustomHtmlModule } from '@igo2/common'; + +@NgModule({ + imports: [ + IgoLanguageModule, + CommonModule, + FormsModule, + MatDialogModule, + IgoInteractiveTourModule, + IgoCustomHtmlModule, + MatButtonModule, + MatTooltipModule, + MatIconModule, + MatToolbarModule + ], + declarations: [WelcomeWindowComponent], + exports: [WelcomeWindowComponent] +}) +export class IgoWelcomeWindowModule {} diff --git a/src/app/pages/portal/welcome-window/welcome-window.service.spec.ts b/src/app/pages/portal/welcome-window/welcome-window.service.spec.ts new file mode 100644 index 000000000..a306aec21 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { WelcomeWindowService } from './welcome-window.service'; + +describe('WelcomeWindowService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: WelcomeWindowService = TestBed.get(WelcomeWindowService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/pages/portal/welcome-window/welcome-window.service.ts b/src/app/pages/portal/welcome-window/welcome-window.service.ts new file mode 100644 index 000000000..88cfee0ef --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.service.ts @@ -0,0 +1,86 @@ +import { Injectable } from '@angular/core'; + +import { ConfigService, StorageService } from '@igo2/core'; +import { MatDialogConfig } from '@angular/material/dialog'; + +@Injectable({ + providedIn: 'root' +}) +export class WelcomeWindowService { + nbVisit: number; + showAgain: boolean; + igoVersionDifferentFromStorage = false; + + constructor( + private configService: ConfigService, + private storageService: StorageService + ) { + this.igoVersionDifferentFromStorage = this.isVersionDifferentFromStorage(); + this.setStorageConfig(); + } + + setStorageConfig() { + this.nbVisit = Number(this.storageService.get('welcomeWindow_nbVisit')); + if (!this.nbVisit) { + this.nbVisit = 0; + } + + this.storageService.set('welcomeWindow_nbVisit', (this.nbVisit += 1)); + this.storageService.set( + 'version', + this.configService.getConfig('version.lib') + ); + } + + isVersionDifferentFromStorage(): boolean { + if ( + this.storageService.get('version') && + this.storageService.get('version') !== + this.configService.getConfig('version.lib') + ) { + return true; + } else { + return false; + } + } + + hasWelcomeWindow(): boolean { + if ( + this.storageService.get('welcomeWindow_showAgain') === false || + this.storageService.get('welcomeWindow_showAgain') === 'false' + ) { + if ( + this.nbVisit >= + this.configService.getConfig('welcomeWindow.nbVisitToShowAgain') + ) { + this.storageService.set('welcomeWindow_nbVisit', 0); + this.storageService.remove('welcomeWindow_showAgain'); + return true; + } else if ( + this.configService.getConfig('welcomeWindow.showAgainOnNewIGOVersion') + ) { + if (this.igoVersionDifferentFromStorage) { + return true; + } + } + return false; + } + + return this.configService.getConfig('welcomeWindow'); + } + + getConfig(): MatDialogConfig { + const dialogConfig = new MatDialogConfig(); + + dialogConfig.disableClose = true; + dialogConfig.autoFocus = true; + dialogConfig.maxWidth = '500px'; + dialogConfig.position = { top: '150px' }; + + return dialogConfig; + } + + afterClosedWelcomeWindow() { + this.storageService.set('welcomeWindow_showAgain', this.showAgain); + } +} diff --git a/src/app/pages/portal/welcome-window/welcome-window.theming.scss b/src/app/pages/portal/welcome-window/welcome-window.theming.scss new file mode 100644 index 000000000..58411c973 --- /dev/null +++ b/src/app/pages/portal/welcome-window/welcome-window.theming.scss @@ -0,0 +1,9 @@ +@mixin igo-welcome-window-theming($theme) { + $primary: map-get($theme, primary); + + .mat-dialog-container { + z-index: 1000; + box-shadow: 0 0 0 1000em rgba(0, 0, 0, 0.6); + transform: translate(-50%, 0); + } +} diff --git a/src/config/config.json b/src/config/config.json index d26ba8d0b..5fa9cef29 100644 --- a/src/config/config.json +++ b/src/config/config.json @@ -11,5 +11,10 @@ "cadastre": { "enabled": true } + }, + "title": "IGO Démo", + "welcomeWindow": { + "showAgainOnNewIGOVersion": true, + "nbVisitToShowAgain": 30 } } diff --git a/src/config/interactiveTour.json b/src/config/interactiveTour.json new file mode 100644 index 000000000..26b178ce1 --- /dev/null +++ b/src/config/interactiveTour.json @@ -0,0 +1,181 @@ +{ + "global": { + "title": "interactiveTour.global.title", + "position": "auto", + "steps": [ + { + "element": ".menu-button", + "title": "interactiveTour.global.menu-button-title", + "text": "interactiveTour.global.menu-button", + "onHide": { + "action": "click", + "condition": "app-sidenav:not([ng-reflect-opened=true])" + } + }, + { + "element": ".igo-search-bar-container", + "title": "interactiveTour.global.search-bar-title", + "text": "interactiveTour.global.search-bar" + }, + { + "element": ".search-bar-buttons", + "title": "interactiveTour.global.search-settings-title", + "text": "interactiveTour.global.search-settings", + "disableInteraction": true + }, + { + "element": "igo-toolbox", + "title": "interactiveTour.global.toolbox-title", + "text": "interactiveTour.global.toolbox", + "beforeShow": { + "element": "#homeButton", + "action": "click" + } + }, + { + "element": "igo-actionbar-item:nth-child(2) mat-list-item", + "title": "interactiveTour.global.context-title", + "text": "interactiveTour.global.context", + "disableInteraction": true, + "beforeShow": { + "element": "#homeButton", + "action": "click" + }, + "beforeChange": { + "action": "click", + "waitFor": ".igo-tool-container" + } + }, + { + "element": ".igo-tool-container", + "title": "interactiveTour.global.context-title", + "text": "interactiveTour.global.context-list", + "disableInteraction": true + }, + { + "element": "igo-context-item", + "title": "interactiveTour.global.context-title", + "text": "interactiveTour.global.context-item" + }, + { + "element": "igo-context-item:nth-of-type(3)", + "title": "interactiveTour.global.context-title", + "text": "interactiveTour.global.context-filter" + }, + { + "element": "igo-context-item:nth-of-type(3)", + "title": "interactiveTour.global.context-title", + "text": "interactiveTour.global.context-filter-click", + "beforeChange": { + "action": "click", + "waitFor": "igo-list" + } + }, + { + "element": "igo-list", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layers", + "noBackButton": true, + "beforeShow": { + "action": "click", + "element": "igo-actionbar-item:nth-child(3) mat-list-item" + } + }, + { + "element": "igo-list", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layers-visible" + }, + { + "element": "igo-layer-item:nth-of-type(2)", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layer-embacle" + }, + { + "element": "igo-layer-item:nth-of-type(2) h4", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.legend", + "onShow": { + "action": "click" + } + }, + { + "element": "igo-layer-item:nth-child(2) button", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layer-eye", + "onShow": { + "action": "click" + } + }, + { + "element": "igo-layer-item:nth-child(2) button:nth-of-type(2)", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layer-tool-btn", + "beforeChange": { + "action": "click", + "waitFor": ".igo-layer-actions-container", + "condition": "!.igo-layer-actions-container" + } + }, + { + "element": ".igo-layer-actions-container", + "title": "interactiveTour.global.layers-title", + "text": "interactiveTour.global.layer-tool" + }, + { + "element": "#homeButton", + "title": "interactiveTour.global.menu-button-title", + "text": "interactiveTour.global.home-button", + "onHide": { + "action": "click" + } + }, + { + "element": "#menu-button", + "title": "interactiveTour.global.menu-button-title", + "text": "interactiveTour.global.menu-button-close", + "onHide": { + "action": "click" + } + }, + { + "element": "igo-geolocate-button", + "title": "interactiveTour.global.geolocate-title", + "text": "interactiveTour.global.geolocate" + }, + { + "element": "igo-baselayers-switcher", + "title": "interactiveTour.global.baselayers-title", + "text": "interactiveTour.global.baselayers" + }, + { + "title": "interactiveTour.global.end-title", + "text": "interactiveTour.global.end" + } + ] + }, + + "timeFilter": { + "title": "interactiveTour.timeFilter.title", + "steps": [ + { + "element": "div.igo-panel-title", + "text": "interactiveTour.timeFilter.start" + }, + { + "element": "div.igo-panel-title", + "text": "interactiveTour.timeFilter.end" + } + ] + }, + + "measurer": { + "title": "interactiveTour.measurer.title", + "steps": [ + { + "element": "div.igo-panel-title", + "text": "interactiveTour.measurer.start" + } + ] + } +} diff --git a/src/environments/environment.github.ts b/src/environments/environment.github.ts index 7a8b4fef1..7ace46a81 100644 --- a/src/environments/environment.github.ts +++ b/src/environments/environment.github.ts @@ -17,6 +17,7 @@ interface Environment { language?: LanguageOptions; searchSources?: { [key: string]: SearchSourceOptions }; projections?: Projection[]; + interactiveTour?: { tourInMobile: boolean; pathToConfigFile: string }; }; } @@ -47,6 +48,10 @@ export const environment: Environment = { language: { prefix: './locale/' }, + interactiveTour: { + tourInMobile: true, + pathToConfigFile: './config/interactiveTour.json' + }, searchSources: { nominatim: { available: false diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 9000d8a02..a2430e87c 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -19,6 +19,7 @@ interface Environment { optionsApi?: OptionsApiOptions; projections?: Projection[]; spatialFilter?: SpatialFilterOptions; + interactiveTour?: { tourInMobile: boolean; pathToConfigFile: string }; }; } @@ -34,6 +35,10 @@ export const environment: Environment = { language: { prefix: './locale/' }, + interactiveTour: { + tourInMobile: true, + pathToConfigFile: './config/interactiveTour.json' + }, searchSources: { nominatim: { available: false diff --git a/src/environments/environment.ts b/src/environments/environment.ts index fbf9c2dee..67a02620e 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -26,6 +26,7 @@ interface Environment { language?: LanguageOptions; searchSources?: { [key: string]: SearchSourceOptions }; projections?: Projection[]; + interactiveTour?: { tourInMobile: boolean; pathToConfigFile: string }; }; } @@ -129,13 +130,19 @@ export const environment: Environment = { id: 'tq_swtq', url: 'https://geoegl.msp.gouv.qc.ca/apis/ws/swtq', regFilters: ['limtn_charg'], - groupImpose: { id: 'mix_swtq_gouv', title: 'mix same name layer' } + groupImpose: { + id: 'mix_swtq_gouv', + title: 'mix same name layer' + } }, { id: 'Gououvert', url: 'https://geoegl.msp.gouv.qc.ca/apis/ws/igo_gouvouvert.fcgi', regFilters: ['limtn_charg'], - groupImpose: { id: 'mix_swtq_gouv', title: 'mix same name layer' } + groupImpose: { + id: 'mix_swtq_gouv', + title: 'mix same name layer' + } } ] } @@ -148,6 +155,10 @@ export const environment: Environment = { language: { prefix: './locale/' }, + interactiveTour: { + tourInMobile: true, + pathToConfigFile: './config/interactiveTour.json' + }, importExport: { url: '/apis/ogre' }, diff --git a/src/locale/en.json b/src/locale/en.json index 05dd85064..33ce69881 100644 --- a/src/locale/en.json +++ b/src/locale/en.json @@ -4,6 +4,7 @@ "close": "Close Menu", "open": "Open Menu" }, + "toastPanel": { "title": "Entities found on the map", "listButton": "Return to the list of entities", @@ -21,13 +22,63 @@ "previousFeatureTooltip": "Previous feature ( shortcut = left arrow)", "nextFeatureTooltip": "Next feature ( shortcut = right arrow)" }, - "IGO": "IGO", + "IGO": "IGO Demo", "noEntityStore": "No tables available for this dataset", "coordinates": "Show coordinates,", "googleMap": "Show in Google maps", "googleStreetView": "Show in Google Street view", + "welcomeWindow": { + "html": "

Welcome to IGO the Québec goverment mapping application

", + "title": "", + "closeButton": "Close", + "notShowCheck": "do not show again" + }, "oldBrowser": { "title": "Too old browser", "message": "IGO2 does not fully support your browser. Please update it or use a different one." + }, + "interactiveTour": { + "global": { + "title": "Interactive tour", + "menu-button-title": "MENU", + "menu-button": "Click on the menu button to open the general menu", + "search-bar-title": "SEARCH BAR", + "search-bar": "Find an address, a GPS point, a layer, etc.", + "search-settings-title": "SEARCH SETTINGS", + "search-settings": "Allows you to configure: the type of result, the number of desired results, etc.", + "toolbox-title": "TOOLBOX", + "toolbox": "On selection of the tool, the application switches inside and displays its content", + "context-title": "CONTEXTS", + "context": "A context is a predefined map made up of several layers grouped together according to a theme", + "context-list": "Inside CONTEXTS, the tool displays the list of predefined thematic maps", + "context-item": "The SIMPLE context which represents a theme and contains certain layers", + "context-filter": "The 'FILTRE TEMPOREL' context contains another grouping", + "context-filter-click": "Let's select the 'FILTRE TEMPOREL' context", + "layers-title": "MAP", + "layers": "The application switches inside and displays the layers that make up THE MAP of this context", + "layers-visible": "Note that some layers are active and visible in the map. Non-active / visible layers are indicated by the crossed out black eye", + "layer-embacle": "Layer of ice jams during flooding", + "legend": "By clicking on the title the legend opens", + "layer-eye": "Un clique sur oeil, active ou désactive l'affichage de la couche", + "layer-tool-btn": "A click on eye, activates or deactivates the display of the layer", + "layer-tool": "Tools panel available for this layer", + "home-button": "Back to main menu", + "menu-button-close": "Click to close menu", + "geolocate-title": "GEOLOCATE", + "geolocate": "To zoom the map to your location", + "baselayers-title": "BASELAYERS", + "baselayers": "To change the basemap", + "end-title": "END", + "end": "End of interactive tour" + }, + "timeFilter": { + "title": "TIME FILTER", + "start": "The temporal filter allows to delimit a period of time which will filter the layer and display only the results which correspond to the defined period of time", + "end": "Applies only to certain layers where the filter can be applied and which are present in the map" + }, + "measurer": { + "title": "MEASURER", + "start": "Guided tour of the measuring tool" + } } } diff --git a/src/locale/fr.json b/src/locale/fr.json index 3456584d7..59ce06e8d 100644 --- a/src/locale/fr.json +++ b/src/locale/fr.json @@ -21,13 +21,63 @@ "previousFeatureTooltip": "Élément précédent ( raccouci clavier = flèche gauche )", "nextFeatureTooltip": "Prochain élément ( raccouci clavier = flèche droite )" }, - "IGO": "IGO", + "IGO": "IGO Démo", "noEntityStore": "Aucune table disponible pour ce jeu de données", "coordinates": "Afficher les coordonnées", "googleMap": "Afficher sur Google maps", "googleStreetView": "Afficher sur Google Street view", + "welcomeWindow": { + "html": "

Bienvenue dans IGO, l'application cartographique du gouvernement du Québec

Débutez en sélectionnant un contexte ★

", + "title": "Fenêtre d'accueil", + "closeButton": "Fermer", + "notShowCheck": "ne plus afficher" + }, "oldBrowser": { "title": "Navigateur obsolète", "message": "IGO2 ne supporte pas pleinement votre navigateur. Veuillez le mettre à jour ou utilisez un navigateur différent." + }, + "interactiveTour": { + "global": { + "title": "Tour interatif", + "menu-button-title": "MENU", + "menu-button": "Cliquer sur le bouton menu pour ouvrir le menu général", + "search-bar-title": "BARRE DE RECHERCHE", + "search-bar": "Rechercher une adresse, un point GPS, une couche, etc.", + "search-settings-title": "OPTIONS DE RECHERCHE", + "search-settings": "Permet de configurer: le type de résultat, le nombre de résultats souhaités, etc.", + "toolbox-title": "LES OUTILS", + "toolbox": "Sur sélection de l'outil, l'application bascule à l'intérieur et affiche son contenu", + "context-title": "LES CONTEXTES", + "context": "Un contexte est une carte prédéfinie constituée de plusieurs couches regroupées selon une thématique", + "context-list": "À l'intérieur des CONTEXTES, l'outil affiche la liste des cartes thématiques prédéfinies", + "context-item": "Ici le contexte SIMPLE qui représente une thématique et contient certaines couches", + "context-filter": "Ici le contexte FILTRE TEMPOREL contient un autre regroupement", + "context-filter-click": "Sélectionnons le contexte FILTRE TEMPOREL", + "layers-title": "LA CARTE", + "layers": "L'application bascule à l'intérieur et affiche les couches qui constituent LA CARTE de ce contexte", + "layers-visible": "On remarque que certaines couches sont actives et visibles dans la carte. Les couches non actives/visibles sont indiquées par l'oeil noir barré", + "layer-embacle": "Couche des embâcles lors d'inondation", + "legend": "En cliquant sur le titre la légende ouvre", + "layer-eye": "Un clique sur oeil, active ou désactive l'affichage de la couche", + "layer-tool-btn": "Activer les outils disponibles sur CETTE couche", + "layer-tool": "Panneau des outils disponibles pour cette couche", + "home-button": "Retour au menu principal", + "menu-button-close": "Cliquer pour fermer le menu", + "geolocate-title": "GEOLOCALISATION", + "geolocate": "Pour zoomer la carte sur votre position", + "baselayers-title": "FONDS DE CARTE", + "baselayers": "Changer le fond de carte", + "end-title": "FIN", + "end": "Fin du tour interatif" + }, + "timeFilter": { + "title": "FILTRE TEMPOREL", + "start": "Le filtre temporel permet de délimiter une période de temps qui filtrera la couche et affichera uniquement les résultats qui corespondent à la période de temps définie", + "end": "S'applique uniquement sur certaine couches ou le filtre peut s'appliquer et qui sont présentes dans la carte" + }, + "measurer": { + "title": "MESURE", + "start": "Tour guidé de l'outil de mesure" + } } } diff --git a/src/styles.scss b/src/styles.scss index 5c837c01d..e5ff14baf 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,2 +1,8 @@ @import 'style/reset.scss'; @import 'style/theme.scss'; + +.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) { + opacity: 1; + visibility: visible; + pointer-events: initial; +}