From 0aa9e105127857f75d018ee02af40a3b55ea3207 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Mon, 25 Nov 2024 16:06:03 +0100 Subject: [PATCH 01/13] :heavy_plus_sign: [open-formulieren/open-forms#2177] Added dependencies Added leaflet-draw and react-leaflet-draw as dependencies. These allow easy user interactions with the leaflet map --- package-lock.json | 27 ++++++++++++++++++++++++++- package.json | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 1d373d8be..841ddc5d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "ibantools": "^3.3.0", "immer": "^9.0.6", "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", "leaflet-geosearch": "^3.8.0", "leaflet-gesture-handling": "^1.2.2", "microscope-sass": "^2.0.0", @@ -36,6 +37,7 @@ "react-formio": "^4.3.0", "react-intl": "^6.4.4", "react-leaflet": "4.2.1", + "react-leaflet-draw": "^0.20.4", "react-modal": "3.16.1", "react-number-format": "5.2.2", "react-router-dom": "^6.11.2", @@ -168,7 +170,7 @@ }, "design-tokens": { "name": "@open-formulieren/design-tokens", - "version": "0.55.0", + "version": "0.53.0", "license": "EUPL-1.2", "devDependencies": { "chokidar": "^3.5.3", @@ -19567,6 +19569,12 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, + "node_modules/leaflet-draw": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/leaflet-draw/-/leaflet-draw-1.0.4.tgz", + "integrity": "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ==", + "license": "MIT" + }, "node_modules/leaflet-geosearch": { "version": "3.11.1", "resolved": "https://registry.npmjs.org/leaflet-geosearch/-/leaflet-geosearch-3.11.1.tgz", @@ -23377,6 +23385,23 @@ "react-dom": "^18.0.0" } }, + "node_modules/react-leaflet-draw": { + "version": "0.20.4", + "resolved": "https://registry.npmjs.org/react-leaflet-draw/-/react-leaflet-draw-0.20.4.tgz", + "integrity": "sha512-u5JHdow2Z9G2AveyUEOTWHXhdhzXdEVQifkNfSaVbEn0gvD+2xW03TQN444zVqovDBvIrBcVWo1VajL4zgl6yg==", + "license": "ISC", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash-es": "^4.17.15" + }, + "peerDependencies": { + "leaflet": "^1.8.0", + "leaflet-draw": "^1.0.4", + "prop-types": "^15.5.2", + "react": "^18.0.0", + "react-leaflet": "^4.0.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", diff --git a/package.json b/package.json index cdba8c6f5..28969b34d 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "ibantools": "^3.3.0", "immer": "^9.0.6", "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", "leaflet-geosearch": "^3.8.0", "leaflet-gesture-handling": "^1.2.2", "microscope-sass": "^2.0.0", @@ -51,6 +52,7 @@ "react-formio": "^4.3.0", "react-intl": "^6.4.4", "react-leaflet": "4.2.1", + "react-leaflet-draw": "^0.20.4", "react-modal": "3.16.1", "react-number-format": "5.2.2", "react-router-dom": "^6.11.2", From 3880b0e16c7d73501f28454379a5f6349bd1a4e6 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 08:42:04 +0100 Subject: [PATCH 02/13] :fire: [open-formulieren/open-forms#2177] Removing old map functionality and components --- src/components/Map/index.jsx | 44 +++--------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 35adf77ae..658f4cd06 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -1,9 +1,8 @@ import {GeoSearchControl} from 'leaflet-geosearch'; -import isEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; -import {useCallback, useContext, useEffect} from 'react'; +import {useContext, useEffect} from 'react'; import {defineMessages, useIntl} from 'react-intl'; -import {MapContainer, Marker, TileLayer, useMap, useMapEvent} from 'react-leaflet'; +import {MapContainer, TileLayer, useMap} from 'react-leaflet'; import {useGeolocation} from 'react-use'; import {ConfigContext} from 'Context'; @@ -72,12 +71,6 @@ const LeaftletMap = ({ const defaultCoordinates = useDefaultCoordinates(); const coordinates = markerCoordinates || defaultCoordinates; - const onWrapperMarkerSet = coordinates => { - const coordinatesChanged = !isEqual(markerCoordinates, coordinates); - if (!coordinatesChanged) return; - onMarkerSet(coordinates); - }; - const modifiers = disabled ? ['disabled'] : []; const className = getBEMClassName('leaflet-map', modifiers); @@ -104,7 +97,6 @@ const LeaftletMap = ({ {coordinates ? ( <> - ) : null} - {disabled ? : } + {/*{disabled ? : }*/} {markerCoordinates && markerCoordinates.length && ( @@ -239,24 +231,6 @@ SearchControl.propTypes = { }), }; -const MarkerWrapper = ({position, onMarkerSet, ...props}) => { - const shouldSetMarker = !!(position && position.length === 2); - - useEffect(() => { - if (!shouldSetMarker) return; - if (!onMarkerSet) return; - onMarkerSet(position); - }); - - // only render a marker if we explicitly have a marker - return shouldSetMarker ? : null; -}; - -MarkerWrapper.propTypes = { - position: PropTypes.arrayOf(PropTypes.number), - onMarkerSet: PropTypes.func, -}; - const DisabledMapControls = () => { const map = useMap(); useEffect(() => { @@ -271,16 +245,4 @@ const DisabledMapControls = () => { return null; }; -const CaptureClick = ({setMarker}) => { - useMapEvent('click', event => { - const newLatLng = [event.latlng.lat, event.latlng.lng]; - setMarker(newLatLng); - }); - return null; -}; - -CaptureClick.propTypes = { - setMarker: PropTypes.func.isRequired, -}; - export default LeaftletMap; From 2611ded545ffdeddec5b824d721f2cc5eefa914a Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 11:48:29 +0100 Subject: [PATCH 03/13] :sparkles: [open-formulieren/open-forms#2177] Map component using geoJson instead of coordinates The map component now uses and stores geoJson data, instead of coordinates. This means we can display and work with multiple shapes. One important change is that geoJson saves coordinates as lng-lat, instead of lat-lng --- src/components/Map/Map.stories.jsx | 9 +++- src/components/Map/index.jsx | 81 ++++++++++++++++++++++++++---- src/formio/components/Map.jsx | 8 +-- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/components/Map/Map.stories.jsx b/src/components/Map/Map.stories.jsx index 76b75272d..c69372051 100644 --- a/src/components/Map/Map.stories.jsx +++ b/src/components/Map/Map.stories.jsx @@ -16,7 +16,14 @@ export default { component: LeafletMap, decorators: [withMapLayout, ConfigDecorator], args: { - markerCoordinates: [52.1326332, 5.291266], + geoJsonFeature: { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [5.291266, 52.1326332], + }, + }, defaultCenter: [52.1326332, 5.291266], defaultZoomLevel: 12, disabled: false, diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 658f4cd06..774c3403b 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -1,8 +1,12 @@ +import * as Leaflet from 'leaflet'; +import 'leaflet-draw/dist/leaflet.draw.css'; import {GeoSearchControl} from 'leaflet-geosearch'; +import 'leaflet/dist/leaflet.css'; import PropTypes from 'prop-types'; -import {useContext, useEffect} from 'react'; +import {useContext, useEffect, useRef} from 'react'; import {defineMessages, useIntl} from 'react-intl'; -import {MapContainer, TileLayer, useMap} from 'react-leaflet'; +import {FeatureGroup, MapContainer, TileLayer, useMap} from 'react-leaflet'; +import {EditControl} from 'react-leaflet-draw'; import {useGeolocation} from 'react-use'; import {ConfigContext} from 'Context'; @@ -60,20 +64,39 @@ const useDefaultCoordinates = () => { }; const LeaftletMap = ({ - markerCoordinates, - onMarkerSet, + geoJsonFeature, + onGeoJsonFeatureSet, defaultCenter = DEFAULT_LAT_LNG, defaultZoomLevel = DEFAULT_ZOOM, disabled = false, tileLayerUrl = TILE_LAYER_RD.url, }) => { + const featureGroupRef = useRef(); const intl = useIntl(); const defaultCoordinates = useDefaultCoordinates(); - const coordinates = markerCoordinates || defaultCoordinates; + const coordinates = defaultCoordinates; const modifiers = disabled ? ['disabled'] : []; const className = getBEMClassName('leaflet-map', modifiers); + const onFeatureCreate = event => { + // Remove the old layers and add the new one. + // This limits the amount of features to 1 + const newLayer = event.layer; + featureGroupRef.current?.clearLayers(); + featureGroupRef.current?.addLayer(newLayer); + + onGeoJsonFeatureSet(featureGroupRef.current?.toGeoJSON()); + }; + + useEffect(() => { + if (!featureGroupRef.current) { + return; + } + featureGroupRef.current?.clearLayers(); + Leaflet.geoJSON(geoJsonFeature).addTo(featureGroupRef.current); + }, [geoJsonFeature, featureGroupRef.current]); + return ( <> + + + {coordinates ? ( <> ) : null} console.log('TODO')} options={{ showMarker: false, showPopup: false, @@ -115,16 +156,34 @@ const LeaftletMap = ({ /> {/*{disabled ? : }*/} - {markerCoordinates && markerCoordinates.length && ( - - )} + {/*{markerCoordinates && markerCoordinates.length && (*/} + {/* */} + {/*)}*/} ); }; LeaftletMap.propTypes = { - markerCoordinates: PropTypes.arrayOf(PropTypes.number), - onMarkerSet: PropTypes.func, + geoJsonFeature: PropTypes.shape({ + type: PropTypes.oneOf(['Feature']).isRequired, + properties: PropTypes.object, + geometry: PropTypes.oneOfType([ + PropTypes.shape({ + type: PropTypes.oneOf(['Point']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.number).isRequired, + }), + PropTypes.shape({ + type: PropTypes.oneOf(['LineString']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)).isRequired, + }), + PropTypes.shape({ + type: PropTypes.oneOf(['Polygon']).isRequired, + coordinates: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number))) + .isRequired, + }), + ]).isRequired, + }), + onGeoJsonFeatureSet: PropTypes.func, disabled: PropTypes.bool, tileLayerUrl: PropTypes.string, }; diff --git a/src/formio/components/Map.jsx b/src/formio/components/Map.jsx index 0e75ac9ff..469bf7061 100644 --- a/src/formio/components/Map.jsx +++ b/src/formio/components/Map.jsx @@ -90,7 +90,7 @@ export default class Map extends Field { super.destroy(); } - onMarkerSet(newLatLng) { + onGeoJsonSet(newLatLng) { this.setValue(newLatLng, {modified: true}); } @@ -99,7 +99,7 @@ export default class Map extends Field { const {lat = defaultLat, lng = defaultLon} = this.component?.initialCenter || {}; const defaultCenter = [lat, lng]; - const markerCoordinates = this.getValue(); + const geoJsonFeature = this.getValue(); const container = this.refs.mapContainer; const zoom = this.component.defaultZoom; @@ -110,8 +110,8 @@ export default class Map extends Field { Date: Tue, 10 Dec 2024 11:52:40 +0100 Subject: [PATCH 04/13] :sparkles: [open-formulieren/open-forms#2177] Change map interactions with component property With the new property `interactions` the component can defined the possible map interactions. Currently supporting 'marker', 'polygon' and 'polyline' --- src/components/Map/Map.stories.jsx | 62 +++++++++++++++++++++++++++++- src/components/Map/index.jsx | 22 ++++++++--- src/formio/components/Map.jsx | 1 + src/map/constants.js | 7 +++- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/components/Map/Map.stories.jsx b/src/components/Map/Map.stories.jsx index c69372051..d4aa1cd51 100644 --- a/src/components/Map/Map.stories.jsx +++ b/src/components/Map/Map.stories.jsx @@ -1,4 +1,5 @@ -import {userEvent, within} from '@storybook/test'; +import {expect, fn, userEvent, within} from '@storybook/test'; +import {useState} from 'react'; import {ConfigDecorator} from 'story-utils/decorators'; @@ -11,10 +12,24 @@ const withMapLayout = Story => ( ); +const StorybookLeafletMap = props => { + const [geoJson, setGeoJson] = useState(props?.geoJsonFeature); + const handleGeoJsonChange = args => { + if (props?.onGeoJsonFeatureSet) { + props?.onGeoJsonFeatureSet(args); + } + setGeoJson(args); + }; + return ( + + ); +}; + export default { title: 'Private API / Map', component: LeafletMap, decorators: [withMapLayout, ConfigDecorator], + render: StorybookLeafletMap, args: { geoJsonFeature: { type: 'Feature', @@ -66,3 +81,48 @@ export const MapWithAerialPhotoBackground = { 'https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0/Actueel_orthoHR/EPSG:28992/{z}/{x}/{y}.png', }, }; + +export const MapWithInteractions = { + args: { + geoJsonFeature: undefined, + interactions: { + polygon: true, + polyline: true, + marker: true, + }, + defaultCenter: [52.1326332, 5.291266], + onGeoJsonFeatureSet: fn(), + }, + parameters: { + msw: { + handlers: [mockAddressSearchGet, mockLatLngSearchEmptyGet], + }, + }, + play: async ({canvasElement, step, args}) => { + const canvas = within(canvasElement); + const map = canvasElement.querySelector('.leaflet-pane.leaflet-map-pane'); + + await step('All interactions are available', async () => { + expect(await canvas.findByTitle('Draw a marker')).toBeVisible(); + expect(await canvas.findByTitle('Draw a polygon')).toBeVisible(); + expect(await canvas.findByTitle('Draw a polyline')).toBeVisible(); + }); + + await step('Draw a marker', async () => { + const markerButton = await canvas.findByTitle('Draw a marker'); + await userEvent.click(markerButton); + + await userEvent.click(map, {x: 100, y: 100}); + + expect(await canvas.findByRole('button', {name: 'Marker'})).toBeVisible(); + expect(args.onGeoJsonFeatureSet).toBeCalledWith({ + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [5.287384, 52.134262], + }, + }); + }); + }, +}; diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 774c3403b..3eeafaeeb 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -10,7 +10,13 @@ import {EditControl} from 'react-leaflet-draw'; import {useGeolocation} from 'react-use'; import {ConfigContext} from 'Context'; -import {CRS_RD, DEFAULT_LAT_LNG, DEFAULT_ZOOM, TILE_LAYER_RD} from 'map/constants'; +import { + CRS_RD, + DEFAULT_INTERACTIONS, + DEFAULT_LAT_LNG, + DEFAULT_ZOOM, + TILE_LAYER_RD, +} from 'map/constants'; import {getBEMClassName} from 'utils'; import NearestAddress from './NearestAddress'; @@ -69,6 +75,7 @@ const LeaftletMap = ({ defaultCenter = DEFAULT_LAT_LNG, defaultZoomLevel = DEFAULT_ZOOM, disabled = false, + interactions = DEFAULT_INTERACTIONS, tileLayerUrl = TILE_LAYER_RD.url, }) => { const featureGroupRef = useRef(); @@ -127,10 +134,10 @@ const LeaftletMap = ({ }} draw={{ rectangle: false, - circle: true, - polyline: true, - polygon: true, - marker: true, + circle: false, + polyline: !!interactions?.polyline, + polygon: !!interactions?.polygon, + marker: !!interactions?.marker, circlemarker: false, }} /> @@ -184,6 +191,11 @@ LeaftletMap.propTypes = { ]).isRequired, }), onGeoJsonFeatureSet: PropTypes.func, + interactions: PropTypes.shape({ + polyline: PropTypes.bool, + polygon: PropTypes.bool, + marker: PropTypes.bool, + }), disabled: PropTypes.bool, tileLayerUrl: PropTypes.string, }; diff --git a/src/formio/components/Map.jsx b/src/formio/components/Map.jsx index 469bf7061..f507cb737 100644 --- a/src/formio/components/Map.jsx +++ b/src/formio/components/Map.jsx @@ -114,6 +114,7 @@ export default class Map extends Field { onGeoJsonFeatureSet={this.onGeoJsonSet.bind(this)} defaultCenter={defaultCenter} defaultZoomLevel={zoom || DEFAULT_ZOOM} + interactions={this.component?.interactions} tileLayerUrl={this.component.tileLayerUrl} /> diff --git a/src/map/constants.js b/src/map/constants.js index dd7cd2f6b..82ad32d79 100644 --- a/src/map/constants.js +++ b/src/map/constants.js @@ -6,5 +6,10 @@ import {CRS_RD, TILE_LAYER_RD} from '@open-formulieren/leaflet-tools'; // Roughly the center of the Netherlands const DEFAULT_LAT_LNG = [52.1326332, 5.291266]; const DEFAULT_ZOOM = 13; +const DEFAULT_INTERACTIONS = { + marker: true, + polygon: false, + polyline: false, +}; -export {CRS_RD, DEFAULT_LAT_LNG, DEFAULT_ZOOM, TILE_LAYER_RD}; +export {CRS_RD, DEFAULT_LAT_LNG, DEFAULT_ZOOM, DEFAULT_INTERACTIONS, TILE_LAYER_RD}; From 3a05f6c19f99f414c755063f7b753022f206324c Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 12:40:17 +0100 Subject: [PATCH 05/13] :sparkles: [open-formulieren/open-forms#2177] Map search control with geoJson --- src/components/Map/index.jsx | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 3eeafaeeb..841083f31 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -87,22 +87,22 @@ const LeaftletMap = ({ const className = getBEMClassName('leaflet-map', modifiers); const onFeatureCreate = event => { - // Remove the old layers and add the new one. - // This limits the amount of features to 1 - const newLayer = event.layer; - featureGroupRef.current?.clearLayers(); - featureGroupRef.current?.addLayer(newLayer); + onGeoJsonFeatureSet(event.layer.toGeoJSON()); + }; - onGeoJsonFeatureSet(featureGroupRef.current?.toGeoJSON()); + const onSearchMarkerSet = event => { + onGeoJsonFeatureSet(event.marker.toGeoJSON()); }; useEffect(() => { if (!featureGroupRef.current) { return; } + // Remove the old layers and add the new one. + // This limits the amount of features to 1 featureGroupRef.current?.clearLayers(); - Leaflet.geoJSON(geoJsonFeature).addTo(featureGroupRef.current); - }, [geoJsonFeature, featureGroupRef.current]); + featureGroupRef.current?.addLayer(Leaflet.geoJSON(geoJsonFeature)); + }); return ( <> @@ -148,7 +148,7 @@ const LeaftletMap = ({ ) : null} console.log('TODO')} + onMarkerSet={onSearchMarkerSet} options={{ showMarker: false, showPopup: false, @@ -235,15 +235,6 @@ const SearchControl = ({onMarkerSet, options}) => { const buttonLabel = intl.formatMessage(searchControlMessages.buttonLabel); - const setMarker = useCallback( - result => { - if (result.location) { - onMarkerSet([result.location.y, result.location.x]); - } - }, - [onMarkerSet] - ); - useEffect(() => { const provider = new OpenFormsProvider(baseUrl); const searchControl = new GeoSearchControl({ @@ -262,15 +253,15 @@ const SearchControl = ({onMarkerSet, options}) => { searchControl.button.setAttribute('aria-label', buttonLabel); map.addControl(searchControl); - map.on('geosearch/showlocation', setMarker); + map.on('geosearch/showlocation', onMarkerSet); return () => { - map.off('geosearch/showlocation', setMarker); + map.off('geosearch/showlocation', onMarkerSet); map.removeControl(searchControl); }; }, [ map, - setMarker, + onMarkerSet, baseUrl, showMarker, showPopup, From 6aeb28047c3f5bffd7667c3783d5aa192128cc10 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 16:37:21 +0100 Subject: [PATCH 06/13] :sparkles: [open-formulieren/open-forms#2177] Get coordinates of geo json --- src/components/Map/index.jsx | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 841083f31..9ace49cc4 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -69,6 +69,15 @@ const useDefaultCoordinates = () => { return [latitude, longitude]; }; +const getCoordinates = geoJsonFeature => { + if (!geoJsonFeature) { + return null; + } + + const center = Leaflet.geoJSON(geoJsonFeature).getBounds().getCenter(); + return [center.lat, center.lng]; +}; + const LeaftletMap = ({ geoJsonFeature, onGeoJsonFeatureSet, @@ -81,7 +90,8 @@ const LeaftletMap = ({ const featureGroupRef = useRef(); const intl = useIntl(); const defaultCoordinates = useDefaultCoordinates(); - const coordinates = defaultCoordinates; + const geoJsonCoordinates = getCoordinates(geoJsonFeature); + const coordinates = geoJsonCoordinates ?? defaultCoordinates; const modifiers = disabled ? ['disabled'] : []; const className = getBEMClassName('leaflet-map', modifiers); @@ -142,11 +152,7 @@ const LeaftletMap = ({ }} /> - {coordinates ? ( - <> - - - ) : null} + {coordinates && } {/*{disabled ? : }*/} - {/*{markerCoordinates && markerCoordinates.length && (*/} - {/* */} - {/*)}*/} + {geoJsonCoordinates && geoJsonCoordinates.length && ( + + )} ); }; From 7381521751826a9aaadbf19a43601baadddcfa47 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 10 Dec 2024 16:39:49 +0100 Subject: [PATCH 07/13] :sparkles: [open-formulieren/open-forms#2177] Disabled logic --- src/components/Map/index.jsx | 66 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 9ace49cc4..cff2fa12b 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -135,39 +135,43 @@ const LeaftletMap = ({ > - + {!disabled && ( + + )} {coordinates && } - - {/*{disabled ? : }*/} + {!disabled && ( + + )} + {disabled && } {geoJsonCoordinates && geoJsonCoordinates.length && ( From 57945a361344d0a64c12fe56922f3bdfe2259fe7 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Thu, 12 Dec 2024 17:37:06 +0100 Subject: [PATCH 08/13] :sparkles: [open-formulieren/open-forms#2177] Display map data on summary view --- src/components/FormStepSummary/ComponentValueDisplay.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FormStepSummary/ComponentValueDisplay.jsx b/src/components/FormStepSummary/ComponentValueDisplay.jsx index 2b491424c..8cddb82b4 100644 --- a/src/components/FormStepSummary/ComponentValueDisplay.jsx +++ b/src/components/FormStepSummary/ComponentValueDisplay.jsx @@ -189,7 +189,7 @@ const MapDisplay = ({component, value}) => { return ; } - return ; + return ; }; const CoSignDisplay = ({value}) => { From 9d0ed640698ae06ebe2de3eec897b3209d8c3378 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Mon, 6 Jan 2025 17:21:21 +0100 Subject: [PATCH 09/13] :green_heart: open-formulieren/open-forms#2177 resolved version issue in package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 841ddc5d6..b5308787c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -170,7 +170,7 @@ }, "design-tokens": { "name": "@open-formulieren/design-tokens", - "version": "0.53.0", + "version": "0.55.0", "license": "EUPL-1.2", "devDependencies": { "chokidar": "^3.5.3", From 5598809aac01babfc737039af21c3ec995d7f22e Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Tue, 7 Jan 2025 10:45:20 +0100 Subject: [PATCH 10/13] :white_check_mark: [open-formulieren/open-forms#2177] Expanding and fixing storybook stories for map component --- src/components/Map/Map.stories.jsx | 34 ++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/components/Map/Map.stories.jsx b/src/components/Map/Map.stories.jsx index d4aa1cd51..b9d9f8029 100644 --- a/src/components/Map/Map.stories.jsx +++ b/src/components/Map/Map.stories.jsx @@ -1,4 +1,4 @@ -import {expect, fn, userEvent, within} from '@storybook/test'; +import {expect, fn, userEvent, waitFor, within} from '@storybook/test'; import {useState} from 'react'; import {ConfigDecorator} from 'story-utils/decorators'; @@ -53,10 +53,17 @@ export default { export const Map = {}; export const MapWithAddressSearch = { - play: async ({canvasElement}) => { + args: { + onGeoJsonFeatureSet: fn(), + }, + play: async ({canvasElement, args}) => { const canvas = within(canvasElement); - const button = await canvas.findByLabelText('Zoeken'); - await userEvent.click(button); + + await waitFor(async () => { + const button = await canvas.findByLabelText('Zoeken'); + await userEvent.click(button); + expect(await canvas.findByPlaceholderText('Zoek adres')).toBeVisible(); + }); const searchField = await canvas.findByPlaceholderText('Zoek adres'); const searchBox = within(searchField.parentNode); @@ -64,6 +71,18 @@ export const MapWithAddressSearch = { const searchResult = await searchBox.findByText('Utrecht, Utrecht, Utrecht'); await userEvent.click(searchResult); + await waitFor(async () => { + // A marker is placed on the search result + expect(args.onGeoJsonFeatureSet).toBeCalledWith({ + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + // To make sure that this test doesn't magically fail, just expect any 2 values + coordinates: [expect.anything(), expect.anything()], + }, + }); + }); }, }; @@ -84,13 +103,11 @@ export const MapWithAerialPhotoBackground = { export const MapWithInteractions = { args: { - geoJsonFeature: undefined, interactions: { polygon: true, polyline: true, marker: true, }, - defaultCenter: [52.1326332, 5.291266], onGeoJsonFeatureSet: fn(), }, parameters: { @@ -120,7 +137,10 @@ export const MapWithInteractions = { properties: {}, geometry: { type: 'Point', - coordinates: [5.287384, 52.134262], + // Expect that the coordinates array contains 2 items. + // We cannot pin it to specific values, because they can differentiate. + // To make sure that this test doesn't magically fail, just expect any 2 values + coordinates: [expect.anything(), expect.anything()], }, }); }); From 17999356226d4957d2d2dbbc61036831be84a4b1 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Thu, 9 Jan 2025 14:10:30 +0100 Subject: [PATCH 11/13] :sparkles: [open-formulieren/open-forms#2177] Allow manually deleting features Due to some formio magic, the `resetValue` flag is needed when performing the `setValue` call. Without this flag it isn't allowed to set a field value to `null` or `undefined`.We also need to use the value `null`, instead of `undefined`. This because setting the value to `undefined` isn't reflected as a value change (what we need, to re-render the component). This is described in node_modules/formiojs/components/_classes/component/Component.js:2524 --- src/components/Map/index.jsx | 33 ++++++++++++++++++++++----------- src/formio/components/Map.jsx | 11 ++++++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index cff2fa12b..dcdbdfb95 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -97,22 +97,33 @@ const LeaftletMap = ({ const className = getBEMClassName('leaflet-map', modifiers); const onFeatureCreate = event => { - onGeoJsonFeatureSet(event.layer.toGeoJSON()); + updateGeoJsonFeature(event.layer); + }; + + const onFeatureDelete = () => { + // The value `null` is needed to make sure that Formio actually updates the value. + // node_modules/formiojs/components/_classes/component/Component.js:2528 + onGeoJsonFeatureSet(null); }; const onSearchMarkerSet = event => { - onGeoJsonFeatureSet(event.marker.toGeoJSON()); + updateGeoJsonFeature(event.marker); }; - useEffect(() => { - if (!featureGroupRef.current) { - return; + const updateGeoJsonFeature = newFeatureLayer => { + if (featureGroupRef.current) { + const newFeatureLayerId = newFeatureLayer._leaflet_id; + const layers = featureGroupRef.current?.getLayers(); + // Limit the amount of features to 1, by removing the previous layer + if (layers.length > 1) { + const oldLayerId = layers + .map(layer => layer._leaflet_id) + .filter(layerId => layerId !== newFeatureLayerId); + featureGroupRef.current?.removeLayer(oldLayerId[0]); + } } - // Remove the old layers and add the new one. - // This limits the amount of features to 1 - featureGroupRef.current?.clearLayers(); - featureGroupRef.current?.addLayer(Leaflet.geoJSON(geoJsonFeature)); - }); + onGeoJsonFeatureSet(newFeatureLayer.toGeoJSON()); + }; return ( <> @@ -139,9 +150,9 @@ const LeaftletMap = ({ Date: Thu, 9 Jan 2025 15:36:35 +0100 Subject: [PATCH 12/13] :lipstick: [open-formulieren/open-forms#2177] Leaflet styling --- src/components/Map/index.jsx | 3 +-- src/components/Map/map.scss | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/components/Map/map.scss diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index dcdbdfb95..2cfc21a3f 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -1,7 +1,5 @@ import * as Leaflet from 'leaflet'; -import 'leaflet-draw/dist/leaflet.draw.css'; import {GeoSearchControl} from 'leaflet-geosearch'; -import 'leaflet/dist/leaflet.css'; import PropTypes from 'prop-types'; import {useContext, useEffect, useRef} from 'react'; import {defineMessages, useIntl} from 'react-intl'; @@ -20,6 +18,7 @@ import { import {getBEMClassName} from 'utils'; import NearestAddress from './NearestAddress'; +import './map.scss'; import OpenFormsProvider from './provider'; const searchControlMessages = defineMessages({ diff --git a/src/components/Map/map.scss b/src/components/Map/map.scss new file mode 100644 index 000000000..d02b5593c --- /dev/null +++ b/src/components/Map/map.scss @@ -0,0 +1,26 @@ +@import 'leaflet/dist/leaflet.css'; +@import 'leaflet-draw/dist/leaflet.draw.css'; + +.leaflet-draw-actions { + margin-top: -1px; + border: 2px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + + // Make sure all action bars are the same height + a, + &.leaflet-draw-actions-top a, + &.leaflet-draw-actions-bottom a { + height: 30px; + line-height: 30px; + } + + a { + background-color: #fff; + border-color: #ccc; + + &:hover, + &:focus { + background-color: #f4f4f4; + } + } +} From bb9305803afba1c5d00163326f0f97c959391fa0 Mon Sep 17 00:00:00 2001 From: robinvandermolen Date: Thu, 9 Jan 2025 15:54:12 +0100 Subject: [PATCH 13/13] :globe_with_meridians: [open-formulieren/open-forms#2177] Leaflet translations --- src/components/Map/index.jsx | 39 ++---- src/components/Map/translations.js | 202 +++++++++++++++++++++++++++++ src/i18n/compiled/en.json | 144 ++++++++++++++++++++ src/i18n/compiled/nl.json | 144 ++++++++++++++++++++ src/i18n/messages/en.json | 120 +++++++++++++++++ src/i18n/messages/nl.json | 120 +++++++++++++++++ 6 files changed, 738 insertions(+), 31 deletions(-) create mode 100644 src/components/Map/translations.js diff --git a/src/components/Map/index.jsx b/src/components/Map/index.jsx index 2cfc21a3f..fcc909b44 100644 --- a/src/components/Map/index.jsx +++ b/src/components/Map/index.jsx @@ -2,7 +2,7 @@ import * as Leaflet from 'leaflet'; import {GeoSearchControl} from 'leaflet-geosearch'; import PropTypes from 'prop-types'; import {useContext, useEffect, useRef} from 'react'; -import {defineMessages, useIntl} from 'react-intl'; +import {useIntl} from 'react-intl'; import {FeatureGroup, MapContainer, TileLayer, useMap} from 'react-leaflet'; import {EditControl} from 'react-leaflet-draw'; import {useGeolocation} from 'react-use'; @@ -20,36 +20,11 @@ import {getBEMClassName} from 'utils'; import NearestAddress from './NearestAddress'; import './map.scss'; import OpenFormsProvider from './provider'; - -const searchControlMessages = defineMessages({ - buttonLabel: { - description: "The leaflet map's search button areaLabel text.", - defaultMessage: 'Map component search button', - }, - searchLabel: { - description: "The leaflet map's input fields placeholder message.", - defaultMessage: 'Enter address, please', - }, - notFound: { - description: "The leaflet map's location not found message.", - defaultMessage: 'Sorry, that address could not be found.', - }, -}); - -const leafletGestureHandlingText = defineMessages({ - touch: { - description: 'Gesturehandeling phone touch message.', - defaultMessage: 'Use two fingers to move the map', - }, - scroll: { - description: 'Gesturehandeling pc scroll message.', - defaultMessage: 'Use ctrl + scroll to zoom the map', - }, - scrollMac: { - description: 'Gesturehandeling mac scroll message.', - defaultMessage: 'Use \u2318 + scroll to zoom the map', - }, -}); +import { + applyLeafletTranslations, + leafletGestureHandlingText, + searchControlMessages, +} from './translations'; const useDefaultCoordinates = () => { // FIXME: can't call hooks conditionally @@ -95,6 +70,8 @@ const LeaftletMap = ({ const modifiers = disabled ? ['disabled'] : []; const className = getBEMClassName('leaflet-map', modifiers); + applyLeafletTranslations(intl); + const onFeatureCreate = event => { updateGeoJsonFeature(event.layer); }; diff --git a/src/components/Map/translations.js b/src/components/Map/translations.js new file mode 100644 index 000000000..3aec19e8c --- /dev/null +++ b/src/components/Map/translations.js @@ -0,0 +1,202 @@ +import * as Leaflet from 'leaflet'; +import {defineMessages} from 'react-intl'; + +const searchControlMessages = defineMessages({ + buttonLabel: { + description: "The leaflet map's search button areaLabel text.", + defaultMessage: 'Map component search button', + }, + searchLabel: { + description: "The leaflet map's input fields placeholder message.", + defaultMessage: 'Enter address, please', + }, + notFound: { + description: "The leaflet map's location not found message.", + defaultMessage: 'Sorry, that address could not be found.', + }, +}); + +const leafletGestureHandlingText = defineMessages({ + touch: { + description: 'Gesturehandeling phone touch message.', + defaultMessage: 'Use two fingers to move the map', + }, + scroll: { + description: 'Gesturehandeling pc scroll message.', + defaultMessage: 'Use ctrl + scroll to zoom the map', + }, + scrollMac: { + description: 'Gesturehandeling mac scroll message.', + defaultMessage: 'Use \u2318 + scroll to zoom the map', + }, +}); + +const leafletEditToolbarMessages = defineMessages({ + saveText: { + description: 'Edit toolbar save message.', + defaultMessage: 'Save', + }, + saveTitle: { + description: 'Edit toolbar save tooltip.', + defaultMessage: 'Save changes', + }, + cancelText: { + description: 'Edit toolbar cancel message.', + defaultMessage: 'Cancel', + }, + cancelTitle: { + description: 'Edit toolbar cancel tooltip.', + defaultMessage: 'Cancel changes', + }, + clearAllText: { + description: 'Edit toolbar clearAll message.', + defaultMessage: 'Remove all', + }, + clearAllTitle: { + description: 'Edit toolbar clearAll tooltip.', + defaultMessage: 'Remove all shapes', + }, + remove: { + description: 'Edit toolbar remove button tooltip.', + defaultMessage: 'Remove shapes', + }, + removeDisabled: { + description: 'Edit toolbar remove button disabled tooltip.', + defaultMessage: 'No shapes to remove', + }, +}); + +const leafletDrawToolbarMessages = defineMessages({ + actionsText: { + description: 'Draw toolbar cancel button message.', + defaultMessage: 'Cancel', + }, + actionsTitle: { + description: 'Draw toolbar cancel button tooltip.', + defaultMessage: 'Cancel drawing', + }, + finishText: { + description: 'Draw toolbar finish button message.', + defaultMessage: 'Finish', + }, + finishTitle: { + description: 'Draw toolbar finish button tooltip.', + defaultMessage: 'Finish drawing', + }, + undoText: { + description: 'Draw toolbar undo button message.', + defaultMessage: 'Remove last point', + }, + undoTitle: { + description: 'Draw toolbar undo button tooltip.', + defaultMessage: 'Remove last drawn point', + }, + polyline: { + description: 'Draw toolbar polyline button tooltip.', + defaultMessage: 'Line', + }, + polygon: { + description: 'Draw toolbar polygon button tooltip.', + defaultMessage: 'Shape (polygon)', + }, + marker: { + description: 'Draw toolbar marker button tooltip.', + defaultMessage: 'Marker', + }, +}); + +const leafletDrawHandlerMessages = defineMessages({ + markerTooltipStart: { + description: 'Draw handler marker tooltip start.', + defaultMessage: 'Click map to place marker', + }, + polylineTooltipStart: { + description: 'Draw handler polyline tooltip start.', + defaultMessage: 'Click to start drawing line', + }, + polylineTooltipContinue: { + description: 'Draw handler polyline tooltip continue.', + defaultMessage: 'Click to continue drawing line', + }, + polylineTooltipEnd: { + description: 'Draw handler polyline tooltip end.', + defaultMessage: 'Click last point to finish line', + }, + polygonTooltipStart: { + description: 'Draw handler polygon tooltip start.', + defaultMessage: 'Click to start drawing shape', + }, + polygonTooltipContinue: { + description: 'Draw handler polygon tooltip continue.', + defaultMessage: 'Click to continue drawing shape', + }, + polygonTooltipEnd: { + description: 'Draw handler polygon tooltip end.', + defaultMessage: 'Click first point to finish shape', + }, +}); + +const applyLeafletTranslations = intl => { + // We have to do the translations via Leaflet + // https://github.com/alex3165/react-leaflet-draw/issues/179 + Leaflet.drawLocal.edit.toolbar = { + actions: { + save: { + text: intl.formatMessage(leafletEditToolbarMessages.saveText), + title: intl.formatMessage(leafletEditToolbarMessages.saveTitle), + }, + cancel: { + text: intl.formatMessage(leafletEditToolbarMessages.cancelText), + title: intl.formatMessage(leafletEditToolbarMessages.cancelTitle), + }, + clearAll: { + text: intl.formatMessage(leafletEditToolbarMessages.clearAllText), + title: intl.formatMessage(leafletEditToolbarMessages.clearAllTitle), + }, + }, + buttons: { + remove: intl.formatMessage(leafletEditToolbarMessages.remove), + removeDisabled: intl.formatMessage(leafletEditToolbarMessages.removeDisabled), + }, + }; + Leaflet.drawLocal.draw.toolbar = { + actions: { + text: intl.formatMessage(leafletDrawToolbarMessages.actionsText), + title: intl.formatMessage(leafletDrawToolbarMessages.actionsTitle), + }, + finish: { + text: intl.formatMessage(leafletDrawToolbarMessages.finishText), + title: intl.formatMessage(leafletDrawToolbarMessages.finishTitle), + }, + undo: { + text: intl.formatMessage(leafletDrawToolbarMessages.undoText), + title: intl.formatMessage(leafletDrawToolbarMessages.undoTitle), + }, + buttons: { + polyline: intl.formatMessage(leafletDrawToolbarMessages.polyline), + polygon: intl.formatMessage(leafletDrawToolbarMessages.polygon), + marker: intl.formatMessage(leafletDrawToolbarMessages.marker), + }, + }; + Leaflet.drawLocal.draw.handlers.marker = { + tooltip: { + start: intl.formatMessage(leafletDrawHandlerMessages.markerTooltipStart), + }, + }; + Leaflet.drawLocal.draw.handlers.polyline = { + tooltip: { + start: intl.formatMessage(leafletDrawHandlerMessages.polylineTooltipStart), + cont: intl.formatMessage(leafletDrawHandlerMessages.polylineTooltipContinue), + end: intl.formatMessage(leafletDrawHandlerMessages.polylineTooltipEnd), + }, + }; + Leaflet.drawLocal.draw.handlers.polygon = { + tooltip: { + start: intl.formatMessage(leafletDrawHandlerMessages.polygonTooltipStart), + cont: intl.formatMessage(leafletDrawHandlerMessages.polygonTooltipContinue), + end: intl.formatMessage(leafletDrawHandlerMessages.polygonTooltipEnd), + }, + }; +}; + +export {searchControlMessages, leafletGestureHandlingText, applyLeafletTranslations}; diff --git a/src/i18n/compiled/en.json b/src/i18n/compiled/en.json index 69d4d9e5d..a8d22545c 100644 --- a/src/i18n/compiled/en.json +++ b/src/i18n/compiled/en.json @@ -43,12 +43,30 @@ "value": "." } ], + "+KBbXH": [ + { + "type": 0, + "value": "Click to start drawing shape" + } + ], + "+W79Dr": [ + { + "type": 0, + "value": "Click to continue drawing line" + } + ], "+uwsgF": [ { "type": 0, "value": "Location and time" } ], + "/yX9Xl": [ + { + "type": 0, + "value": "Cancel drawing" + } + ], "0j8tMq": [ { "type": 0, @@ -81,6 +99,12 @@ "value": "Something went wrong while presenting the login option. Please contact the municipality." } ], + "2D6ubj": [ + { + "type": 0, + "value": "Line" + } + ], "2HHJrq": [ { "type": 0, @@ -117,6 +141,12 @@ "value": "Loading..." } ], + "4ZOmtU": [ + { + "type": 0, + "value": "Save changes" + } + ], "4gPdgK": [ { "type": 0, @@ -129,6 +159,12 @@ "value": "Verify" } ], + "4ui8R+": [ + { + "type": 0, + "value": "Click last point to finish line" + } + ], "5uh2hS": [ { "type": 0, @@ -155,6 +191,12 @@ "value": "Check and co-sign submission" } ], + "62SBjV": [ + { + "type": 0, + "value": "Shape (polygon)" + } + ], "6GNK65": [ { "type": 0, @@ -449,6 +491,12 @@ "value": "provider" } ], + "B/W1uC": [ + { + "type": 0, + "value": "Remove shapes" + } + ], "BL1Ehr": [ { "type": 0, @@ -479,6 +527,12 @@ "value": "This form is currently undergoing maintenance and can not be accessed at the moment." } ], + "D6TdPW": [ + { + "type": 0, + "value": "Click map to place marker" + } + ], "DBTNgI": [ { "type": 0, @@ -665,6 +719,18 @@ "value": "Inloggen bij deze organisatie is niet gelukt. Probeert u het later nog een keer. Lukt het nog steeds niet? Log in bij Mijn DigiD. Zo controleert u of uw DigiD goed werkt. Mogelijk is er een storing bij de organisatie waar u inlogt." } ], + "HY6Irs": [ + { + "type": 0, + "value": "Cancel changes" + } + ], + "HgLr1F": [ + { + "type": 0, + "value": "Cancel" + } + ], "HhcSoc": [ { "type": 0, @@ -677,6 +743,12 @@ "value": "Invalid input." } ], + "Hkq6NE": [ + { + "type": 0, + "value": "Finish drawing" + } + ], "I6ok5N": [ { "type": 0, @@ -973,6 +1045,12 @@ "value": "Payment" } ], + "N2sZnS": [ + { + "type": 0, + "value": "Save" + } + ], "OnfuIr": [ { "options": { @@ -1005,6 +1083,12 @@ "value": "withAsterisk" } ], + "Op4orj": [ + { + "type": 0, + "value": "No shapes to remove" + } + ], "P194bo": [ { "type": 0, @@ -1031,6 +1115,12 @@ "value": "Invalid input." } ], + "Q6/Lj7": [ + { + "type": 0, + "value": "Remove last drawn point" + } + ], "QPdaOi": [ { "type": 0, @@ -1223,6 +1313,12 @@ "value": "." } ], + "U5+FHP": [ + { + "type": 0, + "value": "Cancel" + } + ], "UyEKdQ": [ { "type": 0, @@ -1297,6 +1393,12 @@ "value": "." } ], + "Y3jHZm": [ + { + "type": 0, + "value": "Remove last point" + } + ], "YcC4PD": [ { "type": 0, @@ -1423,6 +1525,12 @@ "value": "Contact details" } ], + "bRtoG6": [ + { + "type": 0, + "value": "Remove all" + } + ], "bgfiRG": [ { "type": 0, @@ -1465,6 +1573,12 @@ "value": "Back to form start" } ], + "dNGw+S": [ + { + "type": 0, + "value": "Click to continue drawing shape" + } + ], "eAmrdi": [ { "type": 0, @@ -1483,6 +1597,12 @@ "value": "Processing..." } ], + "fJVa0u": [ + { + "type": 0, + "value": "Marker" + } + ], "fWjfVm": [ { "type": 0, @@ -2075,6 +2195,12 @@ "value": "Find address" } ], + "sYb+eZ": [ + { + "type": 0, + "value": "Remove all shapes" + } + ], "senRv3": [ { "type": 0, @@ -2215,12 +2341,24 @@ "value": "Amount" } ], + "vfKvuS": [ + { + "type": 0, + "value": "Click first point to finish shape" + } + ], "wwsWUp": [ { "type": 0, "value": "How do you rate this form?" } ], + "xh+7Vv": [ + { + "type": 0, + "value": "Finish" + } + ], "y31iyC": [ { "type": 0, @@ -2247,6 +2385,12 @@ "value": "Return to main website" } ], + "zpHAro": [ + { + "type": 0, + "value": "Click to start drawing line" + } + ], "zvoXSU": [ { "type": 0, diff --git a/src/i18n/compiled/nl.json b/src/i18n/compiled/nl.json index a57a85100..d1524570a 100644 --- a/src/i18n/compiled/nl.json +++ b/src/i18n/compiled/nl.json @@ -43,12 +43,30 @@ "value": " actief." } ], + "+KBbXH": [ + { + "type": 0, + "value": "Klik om een veelhoek (polygoon) te tekenen" + } + ], + "+W79Dr": [ + { + "type": 0, + "value": "Klik om verder te tekenen" + } + ], "+uwsgF": [ { "type": 0, "value": "Afspraakdetails" } ], + "/yX9Xl": [ + { + "type": 0, + "value": "Tekeningen annuleren" + } + ], "0j8tMq": [ { "type": 0, @@ -81,6 +99,12 @@ "value": "Er is iets fout gegaan bij het presenteren van de inlogopties. Neem alstublieft contact met ons op." } ], + "2D6ubj": [ + { + "type": 0, + "value": "Lijn" + } + ], "2HHJrq": [ { "type": 0, @@ -117,6 +141,12 @@ "value": "Opties ophalen..." } ], + "4ZOmtU": [ + { + "type": 0, + "value": "Wijzigingen opslaan" + } + ], "4gPdgK": [ { "type": 0, @@ -129,6 +159,12 @@ "value": "Bevestigen" } ], + "4ui8R+": [ + { + "type": 0, + "value": "Klik het laatste punt om de lijn te voltooien" + } + ], "5uh2hS": [ { "type": 0, @@ -155,6 +191,12 @@ "value": "Controle en medeondertekenen inzending" } ], + "62SBjV": [ + { + "type": 0, + "value": "Veelhoek (polygoon)" + } + ], "6GNK65": [ { "type": 0, @@ -449,6 +491,12 @@ "value": "provider" } ], + "B/W1uC": [ + { + "type": 0, + "value": "Verwijder vormen" + } + ], "BL1Ehr": [ { "type": 0, @@ -479,6 +527,12 @@ "value": "Dit formulier is momenteel in onderhoud en daardoor tijdelijk niet beschikbaar." } ], + "D6TdPW": [ + { + "type": 0, + "value": "Klik op de kaart om een pin/punt te plaatsen" + } + ], "DBTNgI": [ { "type": 0, @@ -665,6 +719,18 @@ "value": "Inloggen bij deze organisatie is niet gelukt. Probeert u het later nog een keer. Lukt het nog steeds niet? Log in bij Mijn DigiD. Zo controleert u of uw DigiD goed werkt. Mogelijk is er een storing bij de organisatie waar u inlogt." } ], + "HY6Irs": [ + { + "type": 0, + "value": "Wijzigingen annuleren" + } + ], + "HgLr1F": [ + { + "type": 0, + "value": "Annuleer" + } + ], "HhcSoc": [ { "type": 0, @@ -677,6 +743,12 @@ "value": "Ongeldige invoer." } ], + "Hkq6NE": [ + { + "type": 0, + "value": "Tekening voltooien" + } + ], "I6ok5N": [ { "type": 0, @@ -973,6 +1045,12 @@ "value": "Betalen" } ], + "N2sZnS": [ + { + "type": 0, + "value": "Opslaan" + } + ], "OnfuIr": [ { "options": { @@ -1005,6 +1083,12 @@ "value": "withAsterisk" } ], + "Op4orj": [ + { + "type": 0, + "value": "Geen vormen om te verwijderen" + } + ], "P194bo": [ { "type": 0, @@ -1031,6 +1115,12 @@ "value": "Ongeldige invoer." } ], + "Q6/Lj7": [ + { + "type": 0, + "value": "Verwijder het laatst getekende punt" + } + ], "QPdaOi": [ { "type": 0, @@ -1223,6 +1313,12 @@ "value": " lang zijn." } ], + "U5+FHP": [ + { + "type": 0, + "value": "Annuleer" + } + ], "UyEKdQ": [ { "type": 0, @@ -1297,6 +1393,12 @@ "value": " zijn." } ], + "Y3jHZm": [ + { + "type": 0, + "value": "Verwijder laatste punt" + } + ], "YcC4PD": [ { "type": 0, @@ -1427,6 +1529,12 @@ "value": "Je gegevens" } ], + "bRtoG6": [ + { + "type": 0, + "value": "Verwijder alles" + } + ], "bgfiRG": [ { "type": 0, @@ -1469,6 +1577,12 @@ "value": "Terug naar begin" } ], + "dNGw+S": [ + { + "type": 0, + "value": "Klik om verder te tekenen" + } + ], "eAmrdi": [ { "type": 0, @@ -1487,6 +1601,12 @@ "value": "Bezig..." } ], + "fJVa0u": [ + { + "type": 0, + "value": "Pin/punt" + } + ], "fWjfVm": [ { "type": 0, @@ -2079,6 +2199,12 @@ "value": "Zoek adres" } ], + "sYb+eZ": [ + { + "type": 0, + "value": "Verwijder alle vormen van de kaart" + } + ], "senRv3": [ { "type": 0, @@ -2219,12 +2345,24 @@ "value": "Aantal personen" } ], + "vfKvuS": [ + { + "type": 0, + "value": "Klik op het eerste punt, om de veelhoek te voltooien" + } + ], "wwsWUp": [ { "type": 0, "value": "Wat vind je van dit formulier?" } ], + "xh+7Vv": [ + { + "type": 0, + "value": "Voltooi" + } + ], "y31iyC": [ { "type": 0, @@ -2251,6 +2389,12 @@ "value": "Terug naar de website" } ], + "zpHAro": [ + { + "type": 0, + "value": "Klik om een lijk te tekenen" + } + ], "zvoXSU": [ { "type": 0, diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 965f9c360..c075c5ed0 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -9,11 +9,26 @@ "description": "Form save modal body text", "originalDefault": "Enter your email address to get an email to resume the form at a later date. This can be done on any device where you open the link. The link remains valid for {numberOfDays, plural, one {1 day} other {{numberOfDays} days}}." }, + "+KBbXH": { + "defaultMessage": "Click to start drawing shape", + "description": "Draw handler polygon tooltip start.", + "originalDefault": "Click to start drawing shape" + }, + "+W79Dr": { + "defaultMessage": "Click to continue drawing line", + "description": "Draw handler polyline tooltip continue.", + "originalDefault": "Click to continue drawing line" + }, "+uwsgF": { "defaultMessage": "Location and time", "description": "Appointments navbar title for 'location and time' step", "originalDefault": "Location and time" }, + "/yX9Xl": { + "defaultMessage": "Cancel drawing", + "description": "Draw toolbar cancel button tooltip.", + "originalDefault": "Cancel drawing" + }, "0j8tMq": { "defaultMessage": "Log out", "description": "Log out button text", @@ -29,6 +44,11 @@ "description": "Co-sign auth option not available on form", "originalDefault": "Something went wrong while presenting the login option. Please contact the municipality." }, + "2D6ubj": { + "defaultMessage": "Line", + "description": "Draw toolbar polyline button tooltip.", + "originalDefault": "Line" + }, "2HHJrq": { "defaultMessage": "Invalid function arguments", "description": "ZOD 'invalid_arguments' error message", @@ -59,6 +79,11 @@ "description": "(Async) select options loading message", "originalDefault": "Loading..." }, + "4ZOmtU": { + "defaultMessage": "Save changes", + "description": "Edit toolbar save tooltip.", + "originalDefault": "Save changes" + }, "4gPdgK": { "defaultMessage": "Bad", "description": "GovMetric rating text", @@ -69,6 +94,11 @@ "description": "Email verification: verify code button text", "originalDefault": "Verify" }, + "4ui8R+": { + "defaultMessage": "Click last point to finish line", + "description": "Draw handler polyline tooltip end.", + "originalDefault": "Click last point to finish line" + }, "5uh2hS": { "defaultMessage": "Appointment cancellation failed", "description": "Appointment cancellation error message", @@ -84,6 +114,11 @@ "description": "Check overview and co-sign", "originalDefault": "Check and co-sign submission" }, + "62SBjV": { + "defaultMessage": "Shape (polygon)", + "description": "Draw toolbar polygon button tooltip.", + "originalDefault": "Shape (polygon)" + }, "6GNK65": { "defaultMessage": "There was an authentication and/or permission problem.", "description": "Authentication error message", @@ -214,6 +249,11 @@ "description": "Login button label", "originalDefault": "Login with {provider}" }, + "B/W1uC": { + "defaultMessage": "Remove shapes", + "description": "Edit toolbar remove button tooltip.", + "originalDefault": "Remove shapes" + }, "BL1Ehr": { "defaultMessage": "Invalid.", "description": "ZOD 'invalid_string' error message, regex validation", @@ -239,6 +279,11 @@ "description": "Maintenance mode message", "originalDefault": "This form is currently undergoing maintenance and can not be accessed at the moment." }, + "D6TdPW": { + "defaultMessage": "Click map to place marker", + "description": "Draw handler marker tooltip start.", + "originalDefault": "Click map to place marker" + }, "DBTNgI": { "defaultMessage": "d", "description": "Placeholder for day part of a date", @@ -364,6 +409,16 @@ "description": "DigiD error message. MUST BE THIS EXACT STRING!", "originalDefault": "Inloggen bij deze organisatie is niet gelukt. Probeert u het later nog een keer. Lukt het nog steeds niet? Log in bij Mijn DigiD. Zo controleert u of uw DigiD goed werkt. Mogelijk is er een storing bij de organisatie waar u inlogt." }, + "HY6Irs": { + "defaultMessage": "Cancel changes", + "description": "Edit toolbar cancel tooltip.", + "originalDefault": "Cancel changes" + }, + "HgLr1F": { + "defaultMessage": "Cancel", + "description": "Draw toolbar cancel button message.", + "originalDefault": "Cancel" + }, "HhcSoc": { "defaultMessage": "Save and resume later", "description": "Form save modal title", @@ -374,6 +429,11 @@ "description": "ZOD 'too_big' error message, generic", "originalDefault": "Invalid input." }, + "Hkq6NE": { + "defaultMessage": "Finish drawing", + "description": "Draw toolbar finish button tooltip.", + "originalDefault": "Finish drawing" + }, "I6ok5N": { "defaultMessage": "Invalid {validation, select, email {email} url {url} datetime {date and time} ip {IP address} other {validation} }.", "description": "ZOD 'invalid_string' error message, validation other than regex", @@ -484,11 +544,21 @@ "description": "Payment page title", "originalDefault": "Payment" }, + "N2sZnS": { + "defaultMessage": "Save", + "description": "Edit toolbar save message.", + "originalDefault": "Save" + }, "OnfuIr": { "defaultMessage": "{withAsterisk, select, true {} other { (not required)}}", "description": "Form field label, field not required", "originalDefault": "{withAsterisk, select, true {} other { (not required)}}" }, + "Op4orj": { + "defaultMessage": "No shapes to remove", + "description": "Edit toolbar remove button disabled tooltip.", + "originalDefault": "No shapes to remove" + }, "P194bo": { "defaultMessage": "Invalid input: must start with \"{startsWith}\".", "description": "ZOD 'invalid_string' error message, with startsWith", @@ -504,6 +574,11 @@ "description": "ZOD 'too_small' error message, generic", "originalDefault": "Invalid input." }, + "Q6/Lj7": { + "defaultMessage": "Remove last drawn point", + "description": "Draw toolbar undo button tooltip.", + "originalDefault": "Remove last drawn point" + }, "QPdaOi": { "defaultMessage": "No results found", "description": "Select 'no options' message", @@ -574,6 +649,11 @@ "description": "ZOD 'too_small' error message, for strings", "originalDefault": "String must contain {exact, select, true {exactly} other {{inclusive, select, true {at least} other {more than}}} } {minimum, plural, one {{minimum} character} other {{minimum} characters}}." }, + "U5+FHP": { + "defaultMessage": "Cancel", + "description": "Edit toolbar cancel message.", + "originalDefault": "Cancel" + }, "UyEKdQ": { "defaultMessage": "Product {number}/{total}", "description": "Appointments: single product label/header", @@ -619,6 +699,11 @@ "description": "ZOD 'too_big' error message, for dates", "originalDefault": "Date must be smaller than or equal to {maximum}." }, + "Y3jHZm": { + "defaultMessage": "Remove last point", + "description": "Draw toolbar undo button message.", + "originalDefault": "Remove last point" + }, "YcC4PD": { "defaultMessage": "Choose language", "description": "Language selection heading", @@ -669,6 +754,11 @@ "description": "Appointments: contact details step title", "originalDefault": "Contact details" }, + "bRtoG6": { + "defaultMessage": "Remove all", + "description": "Edit toolbar clearAll message.", + "originalDefault": "Remove all" + }, "bgfiRG": { "defaultMessage": "You've started the payment process.", "description": "payment started status", @@ -704,6 +794,11 @@ "description": "return to form start link after 403", "originalDefault": "Back to form start" }, + "dNGw+S": { + "defaultMessage": "Click to continue drawing shape", + "description": "Draw handler polygon tooltip continue.", + "originalDefault": "Click to continue drawing shape" + }, "eAmrdi": { "defaultMessage": "Loading...", "description": "Loading content text", @@ -719,6 +814,11 @@ "description": "Checking background processing status title", "originalDefault": "Processing..." }, + "fJVa0u": { + "defaultMessage": "Marker", + "description": "Draw toolbar marker button tooltip.", + "originalDefault": "Marker" + }, "fWjfVm": { "defaultMessage": "Please hold on while we're processing your submission.", "description": "Checking background processing status body", @@ -989,6 +1089,11 @@ "description": "The leaflet map's input fields placeholder message.", "originalDefault": "Enter address, please" }, + "sYb+eZ": { + "defaultMessage": "Remove all shapes", + "description": "Edit toolbar clearAll tooltip.", + "originalDefault": "Remove all shapes" + }, "senRv3": { "defaultMessage": "You're about to cancel your appointment on {date} at {time}. Please fill out your email address for verification purposes.", "description": "Appointment cancellation body text", @@ -1039,11 +1144,21 @@ "description": "Appointments: product amount field label", "originalDefault": "Amount" }, + "vfKvuS": { + "defaultMessage": "Click first point to finish shape", + "description": "Draw handler polygon tooltip end.", + "originalDefault": "Click first point to finish shape" + }, "wwsWUp": { "defaultMessage": "How do you rate this form?", "description": "GovMetric snippet header", "originalDefault": "How do you rate this form?" }, + "xh+7Vv": { + "defaultMessage": "Finish", + "description": "Draw toolbar finish button message.", + "originalDefault": "Finish" + }, "y31iyC": { "defaultMessage": "Invalid literal value, expected {expected}.", "description": "ZOD 'invalid_literal' error message", @@ -1059,6 +1174,11 @@ "description": "Back to main website link title", "originalDefault": "Return to main website" }, + "zpHAro": { + "defaultMessage": "Click to start drawing line", + "description": "Draw handler polyline tooltip start.", + "originalDefault": "Click to start drawing line" + }, "zvoXSU": { "defaultMessage": "Nearest address:
", "description": "Reverse geocoded address result display", diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index 4ef35f536..b2e835870 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -9,11 +9,26 @@ "description": "Form save modal body text", "originalDefault": "Enter your email address to get an email to resume the form at a later date. This can be done on any device where you open the link. The link remains valid for {numberOfDays, plural, one {1 day} other {{numberOfDays} days}}." }, + "+KBbXH": { + "defaultMessage": "Klik om een veelhoek (polygoon) te tekenen", + "description": "Draw handler polygon tooltip start.", + "originalDefault": "Click to start drawing shape" + }, + "+W79Dr": { + "defaultMessage": "Klik om verder te tekenen", + "description": "Draw handler polyline tooltip continue.", + "originalDefault": "Click to continue drawing line" + }, "+uwsgF": { "defaultMessage": "Afspraakdetails", "description": "Appointments navbar title for 'location and time' step", "originalDefault": "Location and time" }, + "/yX9Xl": { + "defaultMessage": "Tekeningen annuleren", + "description": "Draw toolbar cancel button tooltip.", + "originalDefault": "Cancel drawing" + }, "0j8tMq": { "defaultMessage": "Uitloggen", "description": "Log out button text", @@ -29,6 +44,11 @@ "description": "Co-sign auth option not available on form", "originalDefault": "Something went wrong while presenting the login option. Please contact the municipality." }, + "2D6ubj": { + "defaultMessage": "Lijn", + "description": "Draw toolbar polyline button tooltip.", + "originalDefault": "Line" + }, "2HHJrq": { "defaultMessage": "Ongeldige functie-argumenten", "description": "ZOD 'invalid_arguments' error message", @@ -59,6 +79,11 @@ "description": "(Async) select options loading message", "originalDefault": "Loading..." }, + "4ZOmtU": { + "defaultMessage": "Wijzigingen opslaan", + "description": "Edit toolbar save tooltip.", + "originalDefault": "Save changes" + }, "4gPdgK": { "defaultMessage": "Slecht", "description": "GovMetric rating text", @@ -69,6 +94,11 @@ "description": "Email verification: verify code button text", "originalDefault": "Verify" }, + "4ui8R+": { + "defaultMessage": "Klik het laatste punt om de lijn te voltooien", + "description": "Draw handler polyline tooltip end.", + "originalDefault": "Click last point to finish line" + }, "5uh2hS": { "defaultMessage": "Afspraak annuleren mislukt", "description": "Appointment cancellation error message", @@ -84,6 +114,11 @@ "description": "Check overview and co-sign", "originalDefault": "Check and co-sign submission" }, + "62SBjV": { + "defaultMessage": "Veelhoek (polygoon)", + "description": "Draw toolbar polygon button tooltip.", + "originalDefault": "Shape (polygon)" + }, "6GNK65": { "defaultMessage": "Je moet ingelogd zijn voor deze actie.", "description": "Authentication error message", @@ -216,6 +251,11 @@ "description": "Login button label", "originalDefault": "Login with {provider}" }, + "B/W1uC": { + "defaultMessage": "Verwijder vormen", + "description": "Edit toolbar remove button tooltip.", + "originalDefault": "Remove shapes" + }, "BL1Ehr": { "defaultMessage": "Ongeldig.", "description": "ZOD 'invalid_string' error message, regex validation", @@ -241,6 +281,11 @@ "description": "Maintenance mode message", "originalDefault": "This form is currently undergoing maintenance and can not be accessed at the moment." }, + "D6TdPW": { + "defaultMessage": "Klik op de kaart om een pin/punt te plaatsen", + "description": "Draw handler marker tooltip start.", + "originalDefault": "Click map to place marker" + }, "DBTNgI": { "defaultMessage": "d", "description": "Placeholder for day part of a date", @@ -369,6 +414,16 @@ "isTranslated": true, "originalDefault": "Inloggen bij deze organisatie is niet gelukt. Probeert u het later nog een keer. Lukt het nog steeds niet? Log in bij Mijn DigiD. Zo controleert u of uw DigiD goed werkt. Mogelijk is er een storing bij de organisatie waar u inlogt." }, + "HY6Irs": { + "defaultMessage": "Wijzigingen annuleren", + "description": "Edit toolbar cancel tooltip.", + "originalDefault": "Cancel changes" + }, + "HgLr1F": { + "defaultMessage": "Annuleer", + "description": "Draw toolbar cancel button message.", + "originalDefault": "Cancel" + }, "HhcSoc": { "defaultMessage": "Opslaan en later verdergaan", "description": "Form save modal title", @@ -379,6 +434,11 @@ "description": "ZOD 'too_big' error message, generic", "originalDefault": "Invalid input." }, + "Hkq6NE": { + "defaultMessage": "Tekening voltooien", + "description": "Draw toolbar finish button tooltip.", + "originalDefault": "Finish drawing" + }, "I6ok5N": { "defaultMessage": "Ongeldig{validation, select, email { e-mailadres} url {e URL} datetime {e datumtijd} ip { IP address} other { validation} }.", "description": "ZOD 'invalid_string' error message, validation other than regex", @@ -491,11 +551,21 @@ "description": "Payment page title", "originalDefault": "Payment" }, + "N2sZnS": { + "defaultMessage": "Opslaan", + "description": "Edit toolbar save message.", + "originalDefault": "Save" + }, "OnfuIr": { "defaultMessage": "{withAsterisk, select, true {} other { (niet verplicht)}}", "description": "Form field label, field not required", "originalDefault": "{withAsterisk, select, true {} other { (not required)}}" }, + "Op4orj": { + "defaultMessage": "Geen vormen om te verwijderen", + "description": "Edit toolbar remove button disabled tooltip.", + "originalDefault": "No shapes to remove" + }, "P194bo": { "defaultMessage": "De ingevoerde waarde moet beginnen met \"{startsWith}\".", "description": "ZOD 'invalid_string' error message, with startsWith", @@ -511,6 +581,11 @@ "description": "ZOD 'too_small' error message, generic", "originalDefault": "Invalid input." }, + "Q6/Lj7": { + "defaultMessage": "Verwijder het laatst getekende punt", + "description": "Draw toolbar undo button tooltip.", + "originalDefault": "Remove last drawn point" + }, "QPdaOi": { "defaultMessage": "Geen beschikbare keuzes", "description": "Select 'no options' message", @@ -581,6 +656,11 @@ "description": "ZOD 'too_small' error message, for strings", "originalDefault": "String must contain {exact, select, true {exactly} other {{inclusive, select, true {at least} other {more than}}} } {minimum, plural, one {{minimum} character} other {{minimum} characters}}." }, + "U5+FHP": { + "defaultMessage": "Annuleer", + "description": "Edit toolbar cancel message.", + "originalDefault": "Cancel" + }, "UyEKdQ": { "defaultMessage": "Product {number}/{total}", "description": "Appointments: single product label/header", @@ -627,6 +707,11 @@ "description": "ZOD 'too_big' error message, for dates", "originalDefault": "Date must be smaller than or equal to {maximum}." }, + "Y3jHZm": { + "defaultMessage": "Verwijder laatste punt", + "description": "Draw toolbar undo button message.", + "originalDefault": "Remove last point" + }, "YcC4PD": { "defaultMessage": "Taal kiezen", "description": "Language selection heading", @@ -678,6 +763,11 @@ "description": "Appointments: contact details step title", "originalDefault": "Contact details" }, + "bRtoG6": { + "defaultMessage": "Verwijder alles", + "description": "Edit toolbar clearAll message.", + "originalDefault": "Remove all" + }, "bgfiRG": { "defaultMessage": "Je hebt de betaling gestart.", "description": "payment started status", @@ -713,6 +803,11 @@ "description": "return to form start link after 403", "originalDefault": "Back to form start" }, + "dNGw+S": { + "defaultMessage": "Klik om verder te tekenen", + "description": "Draw handler polygon tooltip continue.", + "originalDefault": "Click to continue drawing shape" + }, "eAmrdi": { "defaultMessage": "Laden...", "description": "Loading content text", @@ -728,6 +823,11 @@ "description": "Checking background processing status title", "originalDefault": "Processing..." }, + "fJVa0u": { + "defaultMessage": "Pin/punt", + "description": "Draw toolbar marker button tooltip.", + "originalDefault": "Marker" + }, "fWjfVm": { "defaultMessage": "Een moment geduld alstublieft. De inzending wordt verwerkt.", "description": "Checking background processing status body", @@ -1001,6 +1101,11 @@ "description": "The leaflet map's input fields placeholder message.", "originalDefault": "Enter address, please" }, + "sYb+eZ": { + "defaultMessage": "Verwijder alle vormen van de kaart", + "description": "Edit toolbar clearAll tooltip.", + "originalDefault": "Remove all shapes" + }, "senRv3": { "defaultMessage": "Je staat op het punt je afspraak op {date} om {time} te annuleren. Vul je e-mailadres in ter controle.", "description": "Appointment cancellation body text", @@ -1051,11 +1156,21 @@ "description": "Appointments: product amount field label", "originalDefault": "Amount" }, + "vfKvuS": { + "defaultMessage": "Klik op het eerste punt, om de veelhoek te voltooien", + "description": "Draw handler polygon tooltip end.", + "originalDefault": "Click first point to finish shape" + }, "wwsWUp": { "defaultMessage": "Wat vind je van dit formulier?", "description": "GovMetric snippet header", "originalDefault": "How do you rate this form?" }, + "xh+7Vv": { + "defaultMessage": "Voltooi", + "description": "Draw toolbar finish button message.", + "originalDefault": "Finish" + }, "y31iyC": { "defaultMessage": "Ongeldige letterlijke waarde, verwachtte {expected}.", "description": "ZOD 'invalid_literal' error message", @@ -1071,6 +1186,11 @@ "description": "Back to main website link title", "originalDefault": "Return to main website" }, + "zpHAro": { + "defaultMessage": "Klik om een lijk te tekenen", + "description": "Draw handler polyline tooltip start.", + "originalDefault": "Click to start drawing line" + }, "zvoXSU": { "defaultMessage": "Adres in de buurt:
", "description": "Reverse geocoded address result display",