Skip to content

Commit

Permalink
Partager une carte (#310)
Browse files Browse the repository at this point in the history
* Creation de la route /embed
* Lire / charger un permalink
* Creer un permalien de partage
* Interface de partage
* Ajout des informations de contact dans l'edito
* TODO : prendre en compte la modification de position d'une couche
  • Loading branch information
lowzonenose authored Sep 27, 2024
1 parent 2df0b96 commit dc2d059
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
],
"css.validate": false,
"scss.validate": false,
"[vue]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
"[vue]": { "editor.defaultFormatter": "stylelint.vscode-stylelint" },
"[css]": { "editor.defaultFormatter": "stylelint.vscode-stylelint" },
}
9 changes: 9 additions & 0 deletions public/data/edito.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"contacts": {
"mail": "[email protected]",
"networks": {
"facebook": "https://www.facebook.com/ignfr",
"twitter": "https://twitter.com/IGNFrance",
"linkedin": "https://fr.linkedin.com/company/ignfrance",
"instagram": "https://www.instagram.com/ign_france/?hl=fr"
}
},
"thematics": [
"Administratif",
"Foncier",
Expand Down
3 changes: 3 additions & 0 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ declare module 'vue' {
DsfrConsent: typeof import('@gouvminint/vue-dsfr')['DsfrConsent']
DsfrFooter: typeof import('@gouvminint/vue-dsfr')['DsfrFooter']
DsfrHeader: typeof import('@gouvminint/vue-dsfr')['DsfrHeader']
DsfrInput: typeof import('@gouvminint/vue-dsfr')['DsfrInput']
DsfrModal: typeof import('@gouvminint/vue-dsfr')['DsfrModal']
DsfrNavigation: typeof import('@gouvminint/vue-dsfr')['DsfrNavigation']
DsfrRadioButtonSet: typeof import('@gouvminint/vue-dsfr')['DsfrRadioButtonSet']
DsfrSearchBar: typeof import('@gouvminint/vue-dsfr')['DsfrSearchBar']
DsfrShare: typeof import('@gouvminint/vue-dsfr')['DsfrShare']
DsfrSideMenu: typeof import('@gouvminint/vue-dsfr')['DsfrSideMenu']
DsfrTabContent: typeof import('@gouvminint/vue-dsfr')['DsfrTabContent']
DsfrTabs: typeof import('@gouvminint/vue-dsfr')['DsfrTabs']
Expand Down Expand Up @@ -62,6 +64,7 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
ScaleLine: typeof import('./components/carte/control/ScaleLine.vue')['default']
SearchEngine: typeof import('./components/carte/control/SearchEngine.vue')['default']
Share: typeof import('./components/carte/control/Share.vue')['default']
StoreData: typeof import('./components/StoreData.vue')['default']
View: typeof import('./components/carte/View.vue')['default']
Zoom: typeof import('./components/carte/control/Zoom.vue')['default']
Expand Down
12 changes: 11 additions & 1 deletion src/components/carte/Controls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import MeasureLength from './control/MeasureLength.vue'
import MeasureArea from './control/MeasureArea.vue'
import MeasureAzimuth from './control/MeasureAzimuth.vue'
import Share from './control/Share.vue'
import { useControls } from '@/composables/controls'
import { useLogger } from 'vue-logger-plugin'
Expand All @@ -38,7 +40,10 @@ const props = defineProps({
const log = useLogger()
log.debug(props.controlOptions);
// liste des options pour les contrôles
// liste des options pour les contrôles;
const shareOptions = {};
const layerSwitcherOptions = {
options: {
position : "top-right",
Expand Down Expand Up @@ -118,6 +123,11 @@ const measureAzimuthOptions = {
>>> sinon, visibility:false
-->
<template>
<Share
v-if="controlOptions"
:visibility="props.controlOptions.includes(useControls.Share.id)"
:share-options="shareOptions"
/>
<LayerSwitcher
v-if="controlOptions"
:visibility="props.controlOptions.includes(useControls.LayerSwitcher.id)"
Expand Down
2 changes: 2 additions & 0 deletions src/components/carte/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ mapStore.setMap(map)

<style>
#map {
position: absolute;
width: 100%;
height: inherit;
}
</style>
8 changes: 8 additions & 0 deletions src/components/carte/control/LayerSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ onMounted(() => {
layerSwitcher.value.on("layerswitcher:extent", onZoomToExtentLayer);
layerSwitcher.value.on("layerswitcher:change:opacity", onChangeOpacityLayer);
layerSwitcher.value.on("layerswitcher:change:visibility", onChangeVisibilityLayer);
layerSwitcher.value.on("layerswitcher:change:position", onChangePositionLayer);
}
})
Expand Down Expand Up @@ -108,6 +109,13 @@ const onChangeVisibilityLayer = (e) => {
visible : e.visibility
});
}
const onChangePositionLayer = (e) => {
var id = dataStore.getLayerIdByName(e.layer.name, e.layer.service);
log.debug("onChangePositionLayer", e);
// TODO
// mise à jour de la position de la couche dans le permalien !
}
</script>

<template>
Expand Down
183 changes: 183 additions & 0 deletions src/components/carte/control/Share.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<script lang="js">
/**
* @description
* Panneau de partage de carte
*
* {@link https://github.com/dnum-mi/vue-dsfr/tree/main/src/components/DsfrButton}
* {@link https://github.com/dnum-mi/vue-dsfr/tree/main/src/components/DsfrModal}
* {@link https://github.com/dnum-mi/vue-dsfr/tree/main/src/components/DsfrShare}
*/
export default {};
</script>

<script lang="js" setup>
import { useDataStore } from '@/stores/dataStore';
import { useMapStore } from '@/stores/mapStore';
const dataStore = useDataStore();
const mapStore = useMapStore();
const props = defineProps({
visibility: Boolean,
shareOptions: Object
});
// paramètres du composant bouton
const btnTitle = "Ouvrir le panneau de partage de carte";
const btnIcon = "fr-icon-link"; // FIXME icone de partage dsfr !?
const btnLabel = "";
// paramètres du composant de la modale
const title = "Partager une carte";
const size = "lg";
const shareModalOpened = ref(false);
const onModalShareOpen = () => {
shareModalOpened.value = true;
};
const onModalShareClose = () => {
shareModalOpened.value = false;
};
// les paramètres du composant de partage
const contacts = dataStore.getContacts();
var mail = {
address : contacts.mail,
subject : "Sujet",
body : "Corps du courriel"
};
const shareMail = {
"to" : `mailto:${mail.address}?subject=${mail.subject}&body=${mail.body}`,
"label" : "Envoyer un mail"
};
const shareNetworks = [
{
"name": "facebook",
"label": "Partager sur Facebook",
"url": contacts.networks.facebook
},
{
"name": "twitter-x",
"label": "Partager sur X (anciennement Twitter)",
"url": contacts.networks.twitter
},
{
"name": "linkedin",
"label": "Partager sur LinkedIn",
"url": contacts.networks.linkedin
},
{
"name": "instagram",
"label": "Partager sur Instagram",
"url": contacts.networks.instagram
}
];
// creation de l'iframe de partage
const iframe = computed(() => {
return `<iframe
width="600" height="400" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"
sandbox="allow-forms allow-scripts allow-same-origin"
src="${mapStore.permalinkShare}"
allowfullscreen>
</iframe>`;
});
const target = ref(null);
onMounted(() => {
nextTick(function () {
//code here
})
});
onBeforeMount(() => {
nextTick(function () {
//code here
})
});
</script>

<template>
<div id="share-container" ref="target">
<DsfrButton
v-if="props.visibility"
id="share-button-position"
class="fr-btn fr-btn--md inline-flex justify-center share-button-size"
:label="btnLabel"
:title="btnTitle"
:icon="btnIcon"
icon-only
no-outline
@click="onModalShareOpen"
/>
<DsfrModal
:opened="shareModalOpened"
:title="title"
:size="size"
@close="onModalShareClose">
<!-- slot : c'est ici que l'on customise le contenu ! -->
<div>
<p>
<DsfrShare
copyLabel="Copier dans le presse-papier"
:mail="shareMail"
:networks="shareNetworks"
title="Partages"
/>
</p>
</div>
<div>
<p>
<DsfrInput
v-model="mapStore.permalink"
label="Lien permanent vers la carte"
placeholder=""
label-visible
readonly
descriptionId=""
/>
</p>
<p>
<DsfrInput
v-model="iframe"
label="Copiez le code HTML pour intégrer la carte dans un site"
placeholder=""
isTextarea="true"
label-visible
readonly
descriptionId=""
style="height: 200px;"
/>
</p>
</div>
</DsfrModal>
</div>
</template>

<style>
.fr-btn--instagram::before {
-webkit-mask-image: url("../../../../node_modules/@gouvfr/dsfr/dist/icons/logo/instagram-line.svg");
mask-image: url("../../../../node_modules/@gouvfr/dsfr/dist/icons/logo/instagram-line.svg");
}
</style>

<style scoped>
#share-container {}
#share-button-position {
position: absolute;
left: 10px;
top: 225px;
z-index: 1000;
}
.share-button-size {
width: 40px;
height: 40px;
color: white;
}
.share-iframe-input {
height: 200px;
}
</style>
15 changes: 14 additions & 1 deletion src/composables/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,19 @@ export const useControls = {
disable: false,
analytic: false
},
Share: {
id: 'Share',
active: true,
disable: false,
analytic: false
},
}

/**
* Obtenir les contrôles par défaut
* @returns
*/
export function getDefaultControls() {
export function useDefaultControls() {
var defaultControls = [];
// récupération des controls par défaut
for (var control in useControls) {
Expand Down Expand Up @@ -230,5 +236,12 @@ export function useControlsMenuOptions() {
hint: 'Mesures',
disabled: useControls.MeasureAzimuth.disable
},
{
label: 'Partager une carte',
id: 'share',
name: useControls.Share.id,
hint: 'Partages',
disabled: useControls.Share.disable
},
].filter(opt => Object.keys(useControls).includes(opt.name))
}
67 changes: 67 additions & 0 deletions src/composables/urlParams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useUrlSearchParams } from '@vueuse/core';

/**
* Lecture du permalink pour y extraire les informations.
* La structure est identique au permalien de la carte
* avec quelques paramètres supplementaires :
* - centre : ...
* - x / y : ...
* - lon / lat : ...
* - layers : ...
* - zoom : ...
* - commentaire : "m" string avec contenu encodé
* - titre : "t" string avec contenu encodé
* - localisation : "g" boolean, ajout d'un icone
* - informations : "i" boolean, ajout d'informations prédéfinies
*
* @example
* http://localhost:5173/cartes.gouv.fr-entree-carto/embed?
* &c=417070.66959457495,5975301.705064449
* &z=10
* &l=GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2$GEOPORTAIL:OGC:WMTS(1;1;0),ACCES.BIOMETHANE$GEOPORTAIL:OGC:WMTS(0.47;1;0)
* &permalink=yes
*
* @see mapStore
*/
export function useUrlParams() {
var params = {};
const urlParams = useUrlSearchParams("history");
if (urlParams) {
const keys = Object.keys(urlParams);
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
switch (key) {
case "c":
var xy = urlParams[key].split(",");
params.x = xy[0];
params.y = xy[1];
params.lon = 0; // on ne fait pas la conversion...
params.lat = 0;
params.center = [params.x, params.y];
break;
case "l":
params.layers = urlParams[key];
break;
case "w":
// FIXME utile ? la liste devrait être fixe...
params.controls = urlParams[key];
break;
case "m":
console.debug("not yet implemented !");
break;
case "t":
console.debug("not yet implemented !");
break;
case "i":
console.debug("not yet implemented !");
break;
case "z":
params.zoom = parseInt(urlParams[key], 10);
break;
default:
break;
}
}
}
return params;
};
Loading

0 comments on commit dc2d059

Please sign in to comment.