diff --git a/bundle-scss.mjs b/bundle-scss.mjs index ac68b6bc..a0a90f59 100644 --- a/bundle-scss.mjs +++ b/bundle-scss.mjs @@ -5,6 +5,10 @@ await esbuild.build({ entryPoints: ['src/index.ts'], outdir: 'lib/css', bundle: true, + loader: { + ".png": "dataurl", + ".svg": "dataurl", + }, minify: false, sourcemap: true, plugins: [sassPlugin()], diff --git a/i18n/messages/en.json b/i18n/messages/en.json index 0c878b23..1eb6f421 100644 --- a/i18n/messages/en.json +++ b/i18n/messages/en.json @@ -204,6 +204,11 @@ "description": "Character count", "originalDefault": "{length} {length, plural, one {character} other {characters}}" }, + "5iMlhl": { + "defaultMessage": "Initial focus", + "description": "Map configuration panel title", + "originalDefault": "Initial focus" + }, "5xNPpS": { "defaultMessage": "Option label", "description": "Accessible label for option label", @@ -284,6 +289,11 @@ "description": "Operator 'subtract' option label", "originalDefault": "Subtract" }, + "9oJuUL": { + "defaultMessage": "Marker", + "description": "Label for 'interactions.marker' builder field", + "originalDefault": "Marker" + }, "AClYLf": { "defaultMessage": "If checked, the current day is an allowed value.", "description": "Tooltip for 'date constraint includeToday' builder validation field", @@ -564,6 +574,11 @@ "description": "Label for 'disableAddingRemovingRows' builder field", "originalDefault": "Disable adding or removing groups" }, + "N2dWW9": { + "defaultMessage": "Available drawing shapes", + "description": "Interaction configuration panel title", + "originalDefault": "Available drawing shapes" + }, "NdaqDN": { "defaultMessage": "Option description ()", "description": "Label for option description location", @@ -679,6 +694,11 @@ "description": "Double column label", "originalDefault": "Double column" }, + "T64zDz": { + "defaultMessage": "Polygon", + "description": "Label for 'interactions.polygon' builder field", + "originalDefault": "Polygon" + }, "TCNvqa": { "defaultMessage": "Minimum date", "description": "Date field 'minDate' fixed value label", @@ -794,6 +814,11 @@ "description": "Tooltip for 'Multiple values' builder field", "originalDefault": "Are there multiple values possible for this field?" }, + "WXpj4j": { + "defaultMessage": "Users can set a marker on the map", + "description": "Tooltip for 'interactions.marker' builder field", + "originalDefault": "Users can set a marker on the map" + }, "WZJG0y": { "defaultMessage": "In the past", "description": "Date constraint mode 'past' label", @@ -914,6 +939,11 @@ "description": "Label for address houseletter", "originalDefault": "House letter addition" }, + "b64egT": { + "defaultMessage": "Users can draw shapes (polygons) on the map", + "description": "Tooltip for 'interactions.polygon' builder field", + "originalDefault": "Users can draw shapes (polygons) on the map" + }, "bYBFBc": { "defaultMessage": "Whether to show this value in the submission summary", "description": "Tooltip for 'showInSummary' builder field", @@ -999,11 +1029,6 @@ "description": "Date field 'minDate' validation panel title", "originalDefault": "Minimum date {configured, select, false {(not set)} other {}}" }, - "e6SsfR": { - "defaultMessage": "Map configuration", - "description": "Map configuration panel title", - "originalDefault": "Map configuration" - }, "eAmrdi": { "defaultMessage": "Loading...", "description": "Loading content text", @@ -1244,6 +1269,11 @@ "description": "Tooltip for 'operator' in relative delta date constraint validation", "originalDefault": "Specify whether to add or subtract a time delta to/from the variable." }, + "mRmbnJ": { + "defaultMessage": "Line", + "description": "Label for 'interactions.polyline' builder field", + "originalDefault": "Line" + }, "mWuCXv": { "defaultMessage": "Optionally provide additional information to explain the meaning of the option.", "description": "Tooltip for option/choice description", @@ -1339,6 +1369,11 @@ "description": "Label for 'NumberOfRows' builder field", "originalDefault": "Number of rows" }, + "pM92DZ": { + "defaultMessage": "Users can draw straight lines on the map", + "description": "Tooltip for 'interactions.polyline' builder field", + "originalDefault": "Users can draw straight lines on the map" + }, "pT5jx/": { "defaultMessage": "Column size on mobile, value between 1 and 4.", "description": "Accessible label for column mobile size", @@ -1464,6 +1499,11 @@ "description": "Tooltip for 'prefill.attribute' builder field", "originalDefault": "Specify the attribute holding the pre-fill data." }, + "wJ7Yha": { + "defaultMessage": "Map settings", + "description": "Component edit form tab title for 'Map settings' tab", + "originalDefault": "Map settings" + }, "wOq8Pb": { "defaultMessage": "Translation for option with value \"{value}\"", "description": "Accessible label for option label translation field", diff --git a/i18n/messages/nl.json b/i18n/messages/nl.json index 692b6b38..b60779b0 100644 --- a/i18n/messages/nl.json +++ b/i18n/messages/nl.json @@ -206,6 +206,11 @@ "description": "Character count", "originalDefault": "{length} {length, plural, one {character} other {characters}}" }, + "5iMlhl": { + "defaultMessage": "Begin focuspunt", + "description": "Map configuration panel title", + "originalDefault": "Initial focus" + }, "5xNPpS": { "defaultMessage": "Optielabel", "description": "Accessible label for option label", @@ -287,6 +292,11 @@ "description": "Operator 'subtract' option label", "originalDefault": "Subtract" }, + "9oJuUL": { + "defaultMessage": "Pin/punt", + "description": "Label for 'interactions.marker' builder field", + "originalDefault": "Marker" + }, "AClYLf": { "defaultMessage": "Indien aangevinkt, dan is de dag op het moment van evaluatie een geldige datum.", "description": "Tooltip for 'date constraint includeToday' builder validation field", @@ -572,6 +582,11 @@ "description": "Label for 'disableAddingRemovingRows' builder field", "originalDefault": "Disable adding or removing groups" }, + "N2dWW9": { + "defaultMessage": "Mogelijke kaartmateriaalvormen", + "description": "Interaction configuration panel title", + "originalDefault": "Available drawing shapes" + }, "NdaqDN": { "defaultMessage": "Waarde-omschrijving ()", "description": "Label for option description location", @@ -688,6 +703,11 @@ "description": "Double column label", "originalDefault": "Double column" }, + "T64zDz": { + "defaultMessage": "Veelhoek (Polygoon)", + "description": "Label for 'interactions.polygon' builder field", + "originalDefault": "Polygon" + }, "TCNvqa": { "defaultMessage": "Minimale datum", "description": "Date field 'minDate' fixed value label", @@ -804,6 +824,11 @@ "description": "Tooltip for 'Multiple values' builder field", "originalDefault": "Are there multiple values possible for this field?" }, + "WXpj4j": { + "defaultMessage": "Gebruikers kunnen een punt aanduiden op de kaart", + "description": "Tooltip for 'interactions.marker' builder field", + "originalDefault": "Users can set a marker on the map" + }, "WZJG0y": { "defaultMessage": "In het verleden", "description": "Date constraint mode 'past' label", @@ -925,6 +950,11 @@ "description": "Label for address houseletter", "originalDefault": "House letter addition" }, + "b64egT": { + "defaultMessage": "Gebruikers kunnen een veelhoek (gesloten vorm die uit rechte lijnen bestaat) tekenen.", + "description": "Tooltip for 'interactions.polygon' builder field", + "originalDefault": "Users can draw shapes (polygons) on the map" + }, "bYBFBc": { "defaultMessage": "Geef aan of deze waarde in het bevestigscherm moet getoond worden.", "description": "Tooltip for 'showInSummary' builder field", @@ -1011,11 +1041,6 @@ "description": "Date field 'minDate' validation panel title", "originalDefault": "Minimum date {configured, select, false {(not set)} other {}}" }, - "e6SsfR": { - "defaultMessage": "Kaartinstellingen", - "description": "Map configuration panel title", - "originalDefault": "Map configuration" - }, "eAmrdi": { "defaultMessage": "Aan het laden...", "description": "Loading content text", @@ -1260,6 +1285,11 @@ "description": "Tooltip for 'operator' in relative delta date constraint validation", "originalDefault": "Specify whether to add or subtract a time delta to/from the variable." }, + "mRmbnJ": { + "defaultMessage": "Lijn", + "description": "Label for 'interactions.polyline' builder field", + "originalDefault": "Line" + }, "mWuCXv": { "defaultMessage": "Je kan een extra omschrijving opnemen om een keuzeoptie te verduidelijken.", "description": "Tooltip for option/choice description", @@ -1357,6 +1387,11 @@ "description": "Label for 'NumberOfRows' builder field", "originalDefault": "Number of rows" }, + "pM92DZ": { + "defaultMessage": "Gebruikers kunnen rechte lijnen tekenen op de kaart.", + "description": "Tooltip for 'interactions.polyline' builder field", + "originalDefault": "Users can draw straight lines on the map" + }, "pT5jx/": { "defaultMessage": "Kolombreedte op mobiel, de waarde moet tussen 1 en 4 liggen.", "description": "Accessible label for column mobile size", @@ -1484,6 +1519,11 @@ "description": "Tooltip for 'prefill.attribute' builder field", "originalDefault": "Specify the attribute holding the pre-fill data." }, + "wJ7Yha": { + "defaultMessage": "Kaartmateriaal instellingen", + "description": "Component edit form tab title for 'Map settings' tab", + "originalDefault": "Map settings" + }, "wOq8Pb": { "defaultMessage": "Vertaling voor het label van de optie met waarde \"{value}\"", "description": "Accessible label for option label translation field", diff --git a/package-lock.json b/package-lock.json index dab60662..a569b5de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,11 @@ "clsx": "^1.2.1", "formik": "^2.4.5", "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", "lodash": "^4.17.21", "react-intl": "^6.3.2", "react-leaflet": "^4.2.1", + "react-leaflet-draw": "^0.20.4", "react-modal": "^3.16.1", "react-select": "^5.8.0", "react-signature-canvas": "^1.0.6", @@ -39,7 +41,7 @@ "@formatjs/cli": "^6.1.1", "@formatjs/ts-transformer": "^3.12.0", "@fortawesome/fontawesome-free": "^6.4.0", - "@open-formulieren/types": "^0.37.0", + "@open-formulieren/types": "^0.39.0", "@storybook/addon-actions": "^8.3.5", "@storybook/addon-essentials": "^8.3.5", "@storybook/addon-interactions": "^8.3.5", @@ -5087,9 +5089,9 @@ } }, "node_modules/@open-formulieren/types": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@open-formulieren/types/-/types-0.37.0.tgz", - "integrity": "sha512-8lgFytKGV9AeoUQ1oaYZcsoZoyWe/KoFJKkp66V8QQ9pd/IUt6i92VzTZSOqRr0D3kuMGf7aChXEJOhnaEcWSA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@open-formulieren/types/-/types-0.39.0.tgz", + "integrity": "sha512-hqe4ZSY2Zrp9vupnttyjTsxu5PUi8jlZkhRpObc+hX1GYy30gOtFfifxU6xodeZJ6hzKSCPiUs97bRsxyedisg==", "dev": true, "license": "EUPL-1.2" }, @@ -15850,6 +15852,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/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -17772,6 +17780,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", @@ -24611,9 +24636,9 @@ } }, "@open-formulieren/types": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@open-formulieren/types/-/types-0.37.0.tgz", - "integrity": "sha512-8lgFytKGV9AeoUQ1oaYZcsoZoyWe/KoFJKkp66V8QQ9pd/IUt6i92VzTZSOqRr0D3kuMGf7aChXEJOhnaEcWSA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@open-formulieren/types/-/types-0.39.0.tgz", + "integrity": "sha512-hqe4ZSY2Zrp9vupnttyjTsxu5PUi8jlZkhRpObc+hX1GYy30gOtFfifxU6xodeZJ6hzKSCPiUs97bRsxyedisg==", "dev": true }, "@pkgjs/parseargs": { @@ -32677,6 +32702,11 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, + "leaflet-draw": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/leaflet-draw/-/leaflet-draw-1.0.4.tgz", + "integrity": "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ==" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -34105,6 +34135,15 @@ "@react-leaflet/core": "^2.1.0" } }, + "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==", + "requires": { + "fast-deep-equal": "^3.1.3", + "lodash-es": "^4.17.15" + } + }, "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 34b7ed41..ba2400b5 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@formatjs/cli": "^6.1.1", "@formatjs/ts-transformer": "^3.12.0", "@fortawesome/fontawesome-free": "^6.4.0", - "@open-formulieren/types": "^0.37.0", + "@open-formulieren/types": "^0.39.0", "@storybook/addon-actions": "^8.3.5", "@storybook/addon-essentials": "^8.3.5", "@storybook/addon-interactions": "^8.3.5", @@ -124,9 +124,11 @@ "clsx": "^1.2.1", "formik": "^2.4.5", "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", "lodash": "^4.17.21", "react-intl": "^6.3.2", "react-leaflet": "^4.2.1", + "react-leaflet-draw": "^0.20.4", "react-modal": "^3.16.1", "react-select": "^5.8.0", "react-signature-canvas": "^1.0.6", diff --git a/src/registry/map/edit.tsx b/src/registry/map/edit.tsx index 4ce73b10..4068af7f 100644 --- a/src/registry/map/edit.tsx +++ b/src/registry/map/edit.tsx @@ -21,8 +21,9 @@ import { useDeriveComponentKey, } from '@/components/builder'; import {LABELS} from '@/components/builder/messages'; -import {Checkbox, Select, TabList, TabPanel, Tabs} from '@/components/formio'; +import {Checkbox, Select, Tab, TabList, TabPanel, Tabs} from '@/components/formio'; import {BuilderContext} from '@/context'; +import InteractionConfiguration from '@/registry/map/interaction-configuration'; import {useErrorChecker} from '@/utils/errors'; import {EditFormDefinition} from '../types'; @@ -61,15 +62,25 @@ const EditForm: EditFormDefinition = () => { 'showInPDF', 'hidden', 'clearOnHide', - 'isSensitiveData', - 'useConfigDefaultMapSettings', - 'defaultZoom', - 'initialCenter', - 'tileLayerIdentifier' + 'isSensitiveData' )} /> + + + @@ -84,9 +95,6 @@ const EditForm: EditFormDefinition = () => { - - {!values.useConfigDefaultMapSettings && } - {/* Advanced tab */} @@ -101,6 +109,14 @@ const EditForm: EditFormDefinition = () => { + {/* Map settings tab */} + + + + + {!values.useConfigDefaultMapSettings && } + + {/* Registration tab */} @@ -139,6 +155,11 @@ EditForm.defaultValues = { lng: undefined, }, tileLayerIdentifier: undefined, + interactions: { + polygon: false, + polyline: false, + marker: true, + }, defaultValue: null, // Advanced tab conditional: { diff --git a/src/registry/map/interaction-configuration.tsx b/src/registry/map/interaction-configuration.tsx new file mode 100644 index 00000000..f05cdc2b --- /dev/null +++ b/src/registry/map/interaction-configuration.tsx @@ -0,0 +1,80 @@ +import {FormattedMessage, useIntl} from 'react-intl'; + +import {Checkbox, Panel} from '@/components/formio'; + +const PolygonInteraction: React.FC = () => { + const intl = useIntl(); + const tooltip = intl.formatMessage({ + description: "Tooltip for 'interactions.polygon' builder field", + defaultMessage: 'Users can draw shapes (polygons) on the map', + }); + return ( + + } + tooltip={tooltip} + /> + ); +}; + +const PolylineInteraction: React.FC = () => { + const intl = useIntl(); + const tooltip = intl.formatMessage({ + description: "Tooltip for 'interactions.polyline' builder field", + defaultMessage: 'Users can draw straight lines on the map', + }); + return ( + + } + tooltip={tooltip} + /> + ); +}; + +const MarkerInteraction: React.FC = () => { + const intl = useIntl(); + const tooltip = intl.formatMessage({ + description: "Tooltip for 'interactions.marker' builder field", + defaultMessage: 'Users can set a marker on the map', + }); + return ( + + } + tooltip={tooltip} + /> + ); +}; + +const InteractionConfiguration: React.FC = () => ( + + } + > + + + + +); + +export default InteractionConfiguration; diff --git a/src/registry/map/map-configuration.stories.ts b/src/registry/map/map-configuration.stories.ts index addc53fd..d1eac19a 100644 --- a/src/registry/map/map-configuration.stories.ts +++ b/src/registry/map/map-configuration.stories.ts @@ -45,13 +45,14 @@ export const NotUsingGlobalConfig: Story = { const canvas = within(canvasElement); await step('Initial state', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Map settings'})); expect( canvas.getByLabelText('Use globally configured map component settings') ).not.toBeChecked(); }); await step('Open configuration panel', async () => { - const panelTitle = await canvas.findByText('Map configuration'); + const panelTitle = await canvas.findByText('Initial focus'); await waitFor(async () => { expect(panelTitle).toBeVisible(); }); @@ -81,13 +82,14 @@ export const UsingGlobalConfig: Story = { const canvas = within(canvasElement); await step('Initial state', async () => { + await userEvent.click(canvas.getByRole('link', {name: 'Map settings'})); expect( canvas.getByLabelText('Use globally configured map component settings') ).not.toBeChecked(); }); await step('Toggle checkbox to enable global configuration', async () => { - const panelTitle = await canvas.findByText('Map configuration'); + const panelTitle = await canvas.findByText('Initial focus'); await waitFor(async () => { expect(panelTitle).toBeVisible(); }); diff --git a/src/registry/map/map-configuration.tsx b/src/registry/map/map-configuration.tsx index 9db9f2c8..b55ef190 100644 --- a/src/registry/map/map-configuration.tsx +++ b/src/registry/map/map-configuration.tsx @@ -85,7 +85,7 @@ const MapConfiguration: React.FC = () => ( title={ } collapsible diff --git a/src/registry/map/preview.tsx b/src/registry/map/preview.tsx index b52dbe9a..57084f8d 100644 --- a/src/registry/map/preview.tsx +++ b/src/registry/map/preview.tsx @@ -1,14 +1,17 @@ import {CRS_RD, TILE_LAYER_RD} from '@open-formulieren/leaflet-tools'; import {MapComponentSchema} from '@open-formulieren/types'; -import {useContext, useLayoutEffect} from 'react'; -import {MapContainer, TileLayer, useMap} from 'react-leaflet'; +import type {FeatureGroup as LeafletFeatureGroup} from 'leaflet'; +import {useContext, useLayoutEffect, useRef} from 'react'; +import {FeatureGroup, MapContainer, TileLayer, useMap} from 'react-leaflet'; +import {EditControl} from 'react-leaflet-draw'; import useAsync from 'react-use/esm/useAsync'; import Loader from '@/components/builder/loader'; import {Component, Description} from '@/components/formio'; import {BuilderContext} from '@/context'; +import {ComponentPreviewProps} from '@/registry/types'; -import {ComponentPreviewProps} from '../types'; +import './previews.scss'; interface MapViewProps { lat: number; @@ -41,8 +44,10 @@ const Preview: React.FC> = ({component defaultZoom, initialCenter = {}, tileLayerIdentifier, + interactions, } = component; const {getMapTileLayers} = useContext(BuilderContext); + const featureGroupRef = useRef(null); const {value: tileLayers, loading, error} = useAsync(async () => await getMapTileLayers(), []); if (error) { throw error; @@ -63,6 +68,12 @@ const Preview: React.FC> = ({component TILE_LAYER_RD.url ); }; + + const onFeatureCreate = (event: any) => { + featureGroupRef.current?.clearLayers(); + featureGroupRef.current?.addLayer(event.layer); + }; + return ( > = ({component }} > + + + {description && } diff --git a/src/registry/map/previews.scss b/src/registry/map/previews.scss new file mode 100644 index 00000000..926ff6ce --- /dev/null +++ b/src/registry/map/previews.scss @@ -0,0 +1,2 @@ +@import 'leaflet-draw/dist/leaflet.draw.css'; +@import 'leaflet/dist/leaflet.css'; diff --git a/tsconfig.json b/tsconfig.json index 589fe44a..cc81f0c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "strictNullChecks": true, "allowSyntheticDefaultImports": true, "noErrorTruncation": true, + "skipLibCheck": true, "paths": { "@/*": ["./*"], "@/sb-decorators": ["../.storybook/decorators.tsx"]