diff --git a/frontend/.env.expand b/frontend/.env.expand index c67f303f0f..6f29dccffa 100644 --- a/frontend/.env.expand +++ b/frontend/.env.expand @@ -25,7 +25,6 @@ REACT_APP_IMAGE_UPLOAD_API_URL=$TM_IMAGE_UPLOAD_API_URL REACT_APP_HOMEPAGE_VIDEO_URL=$TM_HOMEPAGE_VIDEO_URL REACT_APP_HOMEPAGE_IMG_HIGH=$TM_HOMEPAGE_IMG_HIGH REACT_APP_HOMEPAGE_IMG_LOW=$TM_HOMEPAGE_IMG_LOW -REACT_APP_MAPBOX_TOKEN=$TM_MAPBOX_TOKEN REACT_APP_ENABLE_SERVICEWORKER=$TM_ENABLE_SERVICEWORKER REACT_APP_MAX_FILESIZE=$TM_IMPORT_MAX_FILESIZE REACT_APP_MAX_AOI_AREA=$TM_MAX_AOI_AREA diff --git a/frontend/package.json b/frontend/package.json index 8ba6792993..fc39f42f0a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,8 +6,6 @@ "dependencies": { "@hotosm/id": "^2.34.0", "@hotosm/iso-countries-languages": "^1.1.2", - "@mapbox/mapbox-gl-draw": "^1.4.3", - "@mapbox/mapbox-gl-language": "^0.10.1", "@maplibre/maplibre-gl-geocoder": "^1.9.0", "@placemarkio/geo-viewport": "^1.0.2", "@rapideditor/rapid": "^2.5.2", @@ -26,6 +24,7 @@ "@turf/transform-scale": "^6.5.0", "@turf/truncate": "^6.5.0", "@uiw/react-md-editor": "^3.22.0", + "@watergis/maplibre-gl-terradraw": "^1.9.5", "axios": "^1.6.7", "chart.js": "^4.4.1", "chartjs-adapter-date-fns": "^3.0.0", @@ -36,7 +35,6 @@ "fromentries": "^1.3.2", "h3-js": "^4.1.0", "humanize-duration": "^3.31.0", - "mapbox-gl-draw-rectangle-mode": "^1.0.4", "maplibre-gl": "^5.6.0", "marked": "^4.3.0", "osmtogeojson": "^3.0.0-beta.5", @@ -69,6 +67,7 @@ "slug": "^8.2.3", "swiper": "^11.1.4", "tachyons": "^4.12.0", + "terra-draw": "^1.18.1", "tributejs": "^5.1.3", "use-query-params": "^2.2.1", "webfontloader": "^1.6.28", diff --git a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js index f12b5307c3..2636558e47 100644 --- a/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js +++ b/frontend/src/components/partnerMapswipeStats/contributionsHeatmap.js @@ -5,12 +5,10 @@ import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import 'maplibre-gl/dist/maplibre-gl.css'; -import { MAPBOX_TOKEN, MAP_STYLE, CHART_COLOURS } from '../../config'; +import { MAP_STYLE, CHART_COLOURS } from '../../config'; import messages from './messages'; import './contributionsHeatmap.css'; -maplibregl.accessToken = MAPBOX_TOKEN; - export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { const mapContainer = useRef(null); const map = useRef(null); @@ -171,7 +169,7 @@ export const ContributionsHeatmap = ({ contributionsByGeo = [] }) => { const currentZoom = map.current.getZoom(); const h3ResBasedOnZoom = currentZoom >= 1 - ? zoomToH3ResMapping[parseInt(currentZoom)] ?? Math.floor((currentZoom - 2) * 0.7) + ? zoomToH3ResMapping[Number.parseInt(currentZoom)] ?? Math.floor((currentZoom - 2) * 0.7) : 1; map.current.getSource('hexbin').setData({ diff --git a/frontend/src/components/partners/currentProjects.js b/frontend/src/components/partners/currentProjects.js index ee88fe296e..dc0de5fb05 100644 --- a/frontend/src/components/partners/currentProjects.js +++ b/frontend/src/components/partners/currentProjects.js @@ -31,7 +31,7 @@ export function CurrentProjects({ currentProjects }) { const fetchData = async () => { try { - const projectIds = currentProjects.split(',').map((id) => parseInt(id.trim(), 10)); + const projectIds = currentProjects.split(',').map((id) => Number.parseInt(id.trim(), 10)); const promises = projectIds.map(async (id) => { const response = await fetch(API_URL + `projects/${id}/tasks/`); diff --git a/frontend/src/components/projectCreate/index.js b/frontend/src/components/projectCreate/index.js index f57681d2d0..85a5d0375f 100644 --- a/frontend/src/components/projectCreate/index.js +++ b/frontend/src/components/projectCreate/index.js @@ -9,8 +9,9 @@ import bbox from '@turf/bbox'; import { featureCollection } from '@turf/helpers'; import truncate from '@turf/truncate'; import toast from 'react-hot-toast'; -import MapboxDraw from '@mapbox/mapbox-gl-draw'; -import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; +import { MaplibreTerradrawControl } from '@watergis/maplibre-gl-terradraw'; +import '@watergis/maplibre-gl-terradraw/dist/maplibre-gl-terradraw.css'; +import { TerraDrawPolygonMode } from 'terra-draw'; import messages from './messages'; import viewsMessages from '../../views/messages'; @@ -33,11 +34,36 @@ import { verifyFileSize, } from '../../utils/geoFileFunctions'; import { getErrorMsg } from './fileUploadErrors'; +import { getAllFeatures, removeFeaturesById } from '../../utils/terrawDraw'; const ProjectCreationMap = lazy(() => import('./projectCreationMap' /* webpackChunkName: "projectCreationMap" */), ); +const polygonModeStyles = { + fillColor: '#F1EE8E', + fillOpacity: 0.3, + outlineColor: '#E69B00', + outlineWidth: 1, + closingPointColor: '#F1EE8E', + closingPointWidth: 1, + closingPointOutlineColor: '#E69B00', + closingPointOutlineWidth: 1, +}; + +//this function Overrides default Terra Draw polygon layer styles to match our theme +const setPolygonStyle = (map) => { + const defaultTdPolygonLayerId = 'td-polygon'; + const defaultTdPolygonOutlineLayerId = 'td-polygon-outline'; + const defaultTdPolygonPointLayerId = 'td-point'; + + map.setPaintProperty(defaultTdPolygonLayerId, 'fill-color', '#F1EE8E'); + map.setPaintProperty(defaultTdPolygonLayerId, 'fill-opacity', 0.3); + map.setPaintProperty(defaultTdPolygonOutlineLayerId, 'line-color', '#E69B00'); + map.setPaintProperty(defaultTdPolygonOutlineLayerId, 'line-width', 1); + map.setPaintProperty(defaultTdPolygonPointLayerId, 'circle-color', '#E69B00'); +}; + const ProjectCreate = () => { const intl = useIntl(); const token = useSelector((state) => state.auth.token); @@ -94,12 +120,8 @@ const ProjectCreate = () => { }; const deleteHandler = () => { - const features = mapObj.draw.getAll(); - if (features.features.length > 0) { - const id = features.features[0].id; - mapObj.draw.delete(id); - } - + const drawInstance = mapObj.draw.getTerraDrawInstance(); + drawInstance.clear(); if (mapObj.map.getSource('aoi')) { mapObj.map.getSource('aoi').setData(featureCollection([])); } @@ -107,27 +129,37 @@ const ProjectCreate = () => { }; const drawHandler = () => { + const drawInstance = mapObj.draw.getTerraDrawInstance(); + if (!drawInstance) return; if (drawModeIsActive) { setDrawModeIsActive(false); - mapObj.draw.changeMode('simple_select'); + drawInstance.setMode('select'); return; } setDrawModeIsActive(true); - const updateArea = (event) => { - const features = mapObj.draw.getAll(); - if (features.features.length > 1) { - const id = features.features[0].id; - mapObj.draw.delete(id); - } - // Validate area first. - setDataGeom(featureCollection(event.features), false); + drawInstance.setMode('polygon'); + drawInstance.on('finish', (id) => { + const allFeatures = getAllFeatures(drawInstance); + // keep the latest one and remove everything else + const previousFeatureIds = allFeatures.reduce( + (prev, curr) => (curr.id !== id ? [...prev, curr.id] : prev), + [], + ); + const newFeature = allFeatures.filter((f) => f.id === id); + + if (previousFeatureIds.length > 0) { + removeFeaturesById(drawInstance, previousFeatureIds); + } + setDataGeom(featureCollection(newFeature), false); + drawInstance.setMode('select'); + drawInstance.selectFeature(id); setDrawModeIsActive(false); - }; - mapObj.map.on('draw.update', updateArea); - mapObj.map.once('draw.create', updateArea); - mapObj.draw.changeMode('draw_polygon'); + // Note: We are manually overriding Terra Draw's default layer paint properties + // because the “select” mode style config may not apply as expected. + setPolygonStyle(mapObj.map); + }); }; // eslint-disable-next-line const [cloneFromId, setCloneFromId] = useQueryParam('cloneFrom', NumberParam); @@ -182,11 +214,16 @@ const ProjectCreate = () => { }, [metadata.area]); const drawOptions = { - displayControlsDefault: false, + modes: ['delete', 'polygon', 'select'], + open: true, + modeOptions: { + polygon: new TerraDrawPolygonMode({ styles: polygonModeStyles }), + }, }; + const [mapObj, setMapObj] = useState({ map: null, - draw: new MapboxDraw(drawOptions), + draw: new MaplibreTerradrawControl(drawOptions), }); const handleCreate = useCallback( diff --git a/frontend/src/components/projectCreate/navButtons.js b/frontend/src/components/projectCreate/navButtons.js index eb80376edf..36524a132f 100644 --- a/frontend/src/components/projectCreate/navButtons.js +++ b/frontend/src/components/projectCreate/navButtons.js @@ -4,6 +4,7 @@ import { FormattedMessage, useIntl } from 'react-intl'; import messages from './messages'; import { Button } from '../button'; import { useAsync } from '../../hooks/UseAsync'; +import { removeFeaturesById } from '../../utils/terrawDraw'; const clearParamsStep = (props) => { switch (props.index) { @@ -58,7 +59,8 @@ const NavButtons = (props) => { return { error: true, message: message }; } else { const id = props.metadata.geom.features[0].id; - props.mapObj.draw.delete(id); + const drawInstance = props.mapObj.draw.getTerraDrawInstance(); + removeFeaturesById(drawInstance, [id]); props.mapObj.map.getSource('aoi').setData(props.metadata.geom); props.updateMetadata({ ...props.metadata, diff --git a/frontend/src/components/projectCreate/projectCreationMap.js b/frontend/src/components/projectCreate/projectCreationMap.js index e2ffeabc58..965aaa061f 100644 --- a/frontend/src/components/projectCreate/projectCreationMap.js +++ b/frontend/src/components/projectCreate/projectCreationMap.js @@ -3,21 +3,11 @@ import { useSelector } from 'react-redux'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { featureCollection } from '@turf/helpers'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; -import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder'; import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css'; import { useDropzone } from 'react-dropzone'; - import { maplibreLayerDefn } from '../projects/projectsMap'; -import useMapboxSupportedLanguage from '../../hooks/UseMapboxSupportedLanguage'; -import { - MAPBOX_TOKEN, - CHART_COLOURS, - TASK_COLOURS, - baseLayers, - DEFAULT_MAP_STYLE, -} from '../../config'; +import { CHART_COLOURS, TASK_COLOURS, baseLayers, DEFAULT_MAP_STYLE } from '../../config'; import { fetchLocalJSONAPI } from '../../network/genericJSONRequest'; import { useDebouncedCallback } from '../../hooks/UseThrottle'; import isWebglSupported from '../../utils/isWebglSupported'; @@ -25,8 +15,7 @@ import useSetRTLTextPlugin from '../../utils/useSetRTLTextPlugin'; import { BasemapMenu } from '../basemapMenu'; import { ProjectsAOILayerCheckBox } from './projectsAOILayerCheckBox'; import WebglUnsupported from '../webglUnsupported'; - -maplibregl.accessToken = MAPBOX_TOKEN; +import '../projectEdit/style.scss'; const ProjectCreationMap = ({ mapObj, @@ -37,7 +26,6 @@ const ProjectCreationMap = ({ uploadFile, }: Object) => { const mapRef = createRef(); - const mapboxSupportedLanguage = useMapboxSupportedLanguage(); const token = useSelector((state) => state.auth.token); const [showProjectsAOILayer, setShowProjectsAOILayer] = useState(true); const [aoiCanBeActivated, setAOICanBeActivated] = useState(false); @@ -93,20 +81,7 @@ const ProjectCreationMap = ({ attributionControl: false, }) .addControl(new maplibregl.AttributionControl({ compact: false })) - .addControl(new MapboxLanguage({ defaultLanguage: mapboxSupportedLanguage })) .addControl(new maplibregl.ScaleControl({ unit: 'metric' })); - if (MAPBOX_TOKEN) { - map.addControl( - new MaplibreGeocoder({ - accessToken: MAPBOX_TOKEN, - maplibregl, - marker: false, - collapsed: true, - language: mapboxSupportedLanguage, - }), - 'top-right', - ); - } setMapObj({ ...mapObj, map: map }); return () => { @@ -264,7 +239,7 @@ const ProjectCreationMap = ({ }); // Remove area and geometry when aoi is deleted. - mapObj.map.on('draw.delete', (event) => { + mapObj.map.on('delete', (event) => { updateMetadata({ ...metadata, geom: null, area: 0 }); }); // enable disable the project AOI visualization checkbox @@ -275,24 +250,6 @@ const ProjectCreationMap = ({ setAOICanBeActivated(true); } }); - - mapObj.map.on('style.load', (event) => { - if (!MAPBOX_TOKEN) { - return; - } - addMapLayers(mapObj.map); - const features = mapObj.draw.getAll(); - if (features.features.length === 0 && mapObj.map.getSource('aoi') !== undefined) { - mapObj.map.getSource('aoi').setData(metadata.geom); - } - - if (metadata.taskGrid && step !== 1 && mapObj.map.getSource('grid') !== undefined) { - mapObj.map.getSource('grid').setData(metadata.taskGrid); - } else { - mapObj.map.getSource('grid') && - mapObj.map.getSource('grid').setData(featureCollection([])); - } - }); } // eslint-disable-next-line }, [mapObj, metadata, updateMetadata, step]); diff --git a/frontend/src/components/projectDetail/index.js b/frontend/src/components/projectDetail/index.js index 6d5fc45898..affcf123d9 100644 --- a/frontend/src/components/projectDetail/index.js +++ b/frontend/src/components/projectDetail/index.js @@ -323,7 +323,7 @@ export const ProjectDetail = (props) => { size={'large'} textColor="white" users={contributors} - maxLength={parseInt(size[0] / 75) > 12 ? 12 : parseInt(size[0] / 75)} + maxLength={Number.parseInt(size[0] / 75) > 12 ? 12 : Number.parseInt(size[0] / 75)} /> )} diff --git a/frontend/src/components/projectEdit/priorityAreasForm.js b/frontend/src/components/projectEdit/priorityAreasForm.js index de201e99bf..97a4011fbc 100644 --- a/frontend/src/components/projectEdit/priorityAreasForm.js +++ b/frontend/src/components/projectEdit/priorityAreasForm.js @@ -1,19 +1,16 @@ import { useState, useContext, useLayoutEffect, createRef } from 'react'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; -import MapboxDraw from '@mapbox/mapbox-gl-draw'; -import DrawRectangle from 'mapbox-gl-draw-rectangle-mode'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; import { featureCollection } from '@turf/helpers'; import { FormattedMessage } from 'react-intl'; import { useDropzone } from 'react-dropzone'; +import { TerraDrawPolygonMode, TerraDrawRectangleMode } from 'terra-draw'; import messages from './messages'; import { StateContext, styleClasses } from '../../views/projectEdit'; import { CustomButton } from '../button'; import { MappedIcon, WasteIcon, MappedSquareIcon, FileImportIcon } from '../svgIcons'; -import { MAPBOX_TOKEN, CHART_COLOURS, baseLayers, DEFAULT_MAP_STYLE } from '../../config'; +import { CHART_COLOURS, baseLayers, DEFAULT_MAP_STYLE } from '../../config'; import { BasemapMenu } from '../basemapMenu'; import { verifyGeometry, @@ -26,47 +23,40 @@ import useSetRTLTextPlugin from '../../utils/useSetRTLTextPlugin'; import { getErrorMsg } from '../projectCreate/fileUploadErrors'; import { Alert } from '../alert'; import WebglUnsupported from '../webglUnsupported'; -import useMapboxSupportedLanguage from '../../hooks/UseMapboxSupportedLanguage'; - -maplibregl.accessToken = MAPBOX_TOKEN; +import { MaplibreTerradrawControl } from '@watergis/maplibre-gl-terradraw'; +import { getAllFeatures } from '../../utils/terrawDraw'; +import './style.scss'; export const PriorityAreasForm = () => { const { projectInfo, setProjectInfo } = useContext(StateContext); - const mapboxSupportedLanguage = useMapboxSupportedLanguage(); const mapRef = createRef(); const [error, setError] = useState({ error: false, message: null }); useSetRTLTextPlugin(); - const modes = MapboxDraw.modes; - modes.draw_rectangle = DrawRectangle; + const styles = { + fillColor: '#d73f3f', + fillOpacity: 0.3, + outlineColor: '#d73f3f', + outlineWidth: 1, + closingPointColor: '#d73f3f', + closingPointWidth: 1, + closingPointOutlineColor: '#d73f3f', + closingPointOutlineWidth: 1, + }; + const drawOptions = { - displayControlsDefault: false, - modes: modes, - styles: [ - { - id: 'gl-draw-polygon-fill-inactive', - type: 'fill', - paint: { - 'fill-color': '#d73f3f', - 'fill-opacity': 0.3, - }, - }, - { - id: 'gl-draw-polygon-line-inactive', - type: 'line', - paint: { - 'line-color': '#d73f3f', - 'line-dasharray': [2, 2], - 'line-width': 2, - 'line-opacity': 0.7, - }, - }, - ], + modes: ['delete', 'polygon', 'select', 'rectangle', 'render'], + open: true, + modeOptions: { + polygon: new TerraDrawPolygonMode({ styles }), + rectangle: new TerraDrawRectangleMode({ styles }), + }, }; + const [mapObj, setMapObj] = useState({ map: null, - draw: new MapboxDraw(drawOptions), + draw: new MaplibreTerradrawControl(drawOptions), }); const [activeMode, setActiveMode] = useState(null); @@ -128,7 +118,6 @@ export const PriorityAreasForm = () => { attributionControl: false, }) .addControl(new maplibregl.AttributionControl({ compact: false })) - .addControl(new MapboxLanguage({ defaultLanguage: mapboxSupportedLanguage })) .addControl(new maplibregl.NavigationControl()); setMapObj({ ...mapObj, map: map }); @@ -177,7 +166,7 @@ export const PriorityAreasForm = () => { source: 'priority_areas', paint: { 'line-color': '#d73f3f', - 'line-dasharray': [2, 2], + // 'line-dasharray': [2, 2], 'line-width': 2, 'line-opacity': 0.7, }, @@ -201,43 +190,51 @@ export const PriorityAreasForm = () => { } }; - const updatePriorityAreas = (event) => { - priorityAreas.push(event.features[0].geometry); + const updatePriorityAreas = (feature) => { + priorityAreas.push(feature[0].geometry); setProjectInfo({ ...projectInfo, priorityAreas: priorityAreas }); setActiveMode(null); }; const drawPolygonHandler = () => { + const drawInstance = mapObj.draw.getTerraDrawInstance(); if (activeMode === 'draw_polygon') { setActiveMode(null); - mapObj.draw.changeMode('simple_select'); + drawInstance.setMode('select'); return; } - + drawInstance.setMode('polygon'); setActiveMode('draw_polygon'); - mapObj.map.on('draw.update', updatePriorityAreas); - mapObj.map.once('draw.create', updatePriorityAreas); - mapObj.draw.changeMode('draw_polygon'); + drawInstance.on('finish', (id) => { + const allFeatures = getAllFeatures(drawInstance); + const newFeature = allFeatures.filter((f) => f.id === id); + updatePriorityAreas(newFeature); + drawInstance.setMode('render'); + }); }; const drawRectangleHandler = () => { + const drawInstance = mapObj.draw.getTerraDrawInstance(); if (activeMode === 'draw_rectangle') { setActiveMode(null); - mapObj.draw.changeMode('simple_select'); + drawInstance.setMode('select'); return; } - + drawInstance.setMode('rectangle'); setActiveMode('draw_rectangle'); - mapObj.map.on('draw.update', updatePriorityAreas); - mapObj.map.once('draw.create', updatePriorityAreas); - mapObj.draw.changeMode('draw_rectangle'); + drawInstance.on('finish', (id) => { + const allFeatures = getAllFeatures(drawInstance); + const newFeature = allFeatures.filter((f) => f.id === id); + updatePriorityAreas(newFeature); + drawInstance.setMode('render'); + }); }; useLayoutEffect(() => { if (mapObj.map !== null && isWebglSupported()) { mapObj.map.on('load', () => { addMapLayers(mapObj.map); - mapObj.map.addControl(mapObj.draw); + mapObj.map.addControl(mapObj.draw, 'bottom-left'); mapObj.map.fitBounds(projectInfo.aoiBBOX, { duration: 0, padding: 100 }); }); @@ -254,9 +251,8 @@ export const PriorityAreasForm = () => { }, [mapObj.map, mapObj.draw, projectInfo, setProjectInfo, drawPriorityAreas]); const clearAll = () => { - const currentDrawMode = mapObj.draw.getMode(); - mapObj.draw.deleteAll(); - mapObj.draw.changeMode(currentDrawMode); + const drawInstance = mapObj.draw.getTerraDrawInstance(); + drawInstance.clear(); mapObj.map.getSource('priority_areas').setData(featureCollection([])); setProjectInfo({ ...projectInfo, priorityAreas: [] }); }; diff --git a/frontend/src/components/projectEdit/style.scss b/frontend/src/components/projectEdit/style.scss new file mode 100644 index 0000000000..a97156ab3e --- /dev/null +++ b/frontend/src/components/projectEdit/style.scss @@ -0,0 +1,3 @@ +.maplibregl-terradraw-add-control, .maplibregl-terradraw-render-button{ + display: none !important; +} diff --git a/frontend/src/components/projects/projectsMap.js b/frontend/src/components/projects/projectsMap.js index dcd5f55feb..5a05348721 100644 --- a/frontend/src/components/projects/projectsMap.js +++ b/frontend/src/components/projects/projectsMap.js @@ -1,23 +1,17 @@ import { createRef, useLayoutEffect, useState, useCallback } from 'react'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; import WebglUnsupported from '../webglUnsupported'; import isWebglSupported from '../../utils/isWebglSupported'; import useSetRTLTextPlugin from '../../utils/useSetRTLTextPlugin'; -import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; +import { MAP_STYLE } from '../../config'; import mapMarker from '../../assets/img/mapMarker.png'; -import useMapboxSupportedLanguage from '../../hooks/UseMapboxSupportedLanguage'; let markerIcon = new Image(17, 20); markerIcon.src = mapMarker; -maplibregl.accessToken = MAPBOX_TOKEN; - -const licensedFonts = MAPBOX_TOKEN - ? ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'] - : ['Open Sans Semibold']; +const licensedFonts = ['Open Sans Semibold']; export const maplibreLayerDefn = (map, mapResults, clickOnProjectID, disablePoiClick = false) => { map.addImage('mapMarker', markerIcon, { width: 15, height: 15, data: markerIcon }); @@ -95,7 +89,6 @@ export const maplibreLayerDefn = (map, mapResults, clickOnProjectID, disablePoiC export const ProjectsMap = ({ mapResults, fullProjectsQuery, setQuery, className }) => { const mapRef = createRef(); const [map, setMapObj] = useState(null); - const mapboxSupportedLanguage = useMapboxSupportedLanguage(); useSetRTLTextPlugin(); const clickOnProjectID = useCallback( @@ -127,9 +120,7 @@ export const ProjectsMap = ({ mapResults, fullProjectsQuery, setQuery, className center: [0, 0], zoom: 0.5, attributionControl: false, - }) - .addControl(new maplibregl.AttributionControl({ compact: false })) - .addControl(new MapboxLanguage({ defaultLanguage: mapboxSupportedLanguage })), + }).addControl(new maplibregl.AttributionControl({ compact: false })), ); return () => { diff --git a/frontend/src/components/taskSelection/map.js b/frontend/src/components/taskSelection/map.js index d313a4cc2c..8840061cbb 100644 --- a/frontend/src/components/taskSelection/map.js +++ b/frontend/src/components/taskSelection/map.js @@ -3,17 +3,15 @@ import { useSelector } from 'react-redux'; import bbox from '@turf/bbox'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; import { FormattedMessage, useIntl } from 'react-intl'; import WebglUnsupported from '../webglUnsupported'; import isWebglSupported from '../../utils/isWebglSupported'; import useSetRTLTextPlugin from '../../utils/useSetRTLTextPlugin'; import messages from './messages'; -import { MAPBOX_TOKEN, TASK_COLOURS, MAP_STYLE } from '../../config'; +import { TASK_COLOURS, MAP_STYLE } from '../../config'; import lock from '../../assets/img/lock.png'; import redlock from '../../assets/img/red-lock.png'; -import useMapboxSupportedLanguage from '../../hooks/UseMapboxSupportedLanguage'; let lockIcon = new Image(17, 20); lockIcon.src = lock; @@ -21,8 +19,6 @@ lockIcon.src = lock; let redlockIcon = new Image(17, 20); redlockIcon.src = redlock; -maplibregl.accessToken = MAPBOX_TOKEN; - export const TasksMap = ({ className, mapResults, @@ -40,7 +36,6 @@ export const TasksMap = ({ }) => { const intl = useIntl(); const mapRef = createRef(); - const mapboxSupportedLanguage = useMapboxSupportedLanguage(); const authDetails = useSelector((state) => state.auth.userDetails); const [hoveredTaskId, setHoveredTaskId] = useState(null); @@ -60,9 +55,7 @@ export const TasksMap = ({ center: [0, 0], zoom: 1, attributionControl: false, - }) - .addControl(new maplibregl.AttributionControl({ compact: false })) - .addControl(new MapboxLanguage({ defaultLanguage: mapboxSupportedLanguage })), + }).addControl(new maplibregl.AttributionControl({ compact: false })), ); return () => { diff --git a/frontend/src/components/teamsAndOrgs/orgUsageLevel.js b/frontend/src/components/teamsAndOrgs/orgUsageLevel.js index 802cb458c2..20378023e0 100644 --- a/frontend/src/components/teamsAndOrgs/orgUsageLevel.js +++ b/frontend/src/components/teamsAndOrgs/orgUsageLevel.js @@ -15,7 +15,7 @@ import { // this component is designed to the FREE organisation type export function OrganisationUsageLevel({ completedActions, orgName }) { const [currentLevel, nextLevelThreshold] = useOrganisationLevel(completedActions); - const percent = parseInt((completedActions / nextLevelThreshold) * 100); + const percent = Number.parseInt((completedActions / nextLevelThreshold) * 100); const yearPrediction = usePredictYearlyTasks(completedActions, new Date()); const levelPrediction = usePredictLevel(yearPrediction, 'FREE'); const currentYear = getYear(new Date()); @@ -110,7 +110,7 @@ export function OrganisationTier({ completedActions, type, subscriptionTier }) { ? levels.filter((level) => level.level === selectedTier.level + 1)[0].minActions : null; const nextLevel = useGetLevel(selectedTierMax); - const percent = parseInt((completedActions / selectedTierMax) * 100); + const percent = Number.parseInt((completedActions / selectedTierMax) * 100); const showDiscountLabel = levelPrediction.tier !== 'free' && type === 'DISCOUNTED'; const currentYear = getYear(new Date()); diff --git a/frontend/src/components/userDetail/countriesMapped.js b/frontend/src/components/userDetail/countriesMapped.js index d5432c0cf1..b7aa3449df 100644 --- a/frontend/src/components/userDetail/countriesMapped.js +++ b/frontend/src/components/userDetail/countriesMapped.js @@ -1,23 +1,18 @@ import { createRef, useLayoutEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import maplibregl from 'maplibre-gl'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; import { FormattedMessage } from 'react-intl'; import messages from './messages'; -import { MAPBOX_TOKEN, MAP_STYLE } from '../../config'; +import { MAP_STYLE } from '../../config'; import { maplibreLayerDefn } from '../projects/projectsMap'; import { BarListChart } from './barListChart'; import WebglUnsupported from '../webglUnsupported'; import isWebglSupported from '../../utils/isWebglSupported'; import useSetRTLTextPlugin from '../../utils/useSetRTLTextPlugin'; -import useMapboxSupportedLanguage from '../../hooks/UseMapboxSupportedLanguage'; - -maplibregl.accessToken = MAPBOX_TOKEN; const UserCountriesMap = ({ projects }) => { const navigate = useNavigate(); - const mapboxSupportedLanguage = useMapboxSupportedLanguage(); const [map, setMap] = useState(null); const mapRef = createRef(); @@ -33,9 +28,7 @@ const UserCountriesMap = ({ projects }) => { center: [0, 0], zoom: 0.5, attributionControl: false, - }) - .addControl(new maplibregl.AttributionControl({ compact: false })) - .addControl(new MapboxLanguage({ defaultLanguage: mapboxSupportedLanguage })), + }).addControl(new maplibregl.AttributionControl({ compact: false })), ); return () => { diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index ce0b525535..a419181226 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -14,10 +14,9 @@ export const DEFAULT_LOCALE = process.env.REACT_APP_DEFAULT_LOCALE || 'en'; export const ENVIRONMENT = process.env.REACT_APP_ENVIRONMENT || ''; export const PROJECTCARD_CONTRIBUTION_SHOWN_THRESHOLD = process.env.REACT_APP_PROJECTCARD_CONTRIBUTION_SHOWN_THRESHOLD || 5; -export const MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_TOKEN || ''; export const ENABLE_SERVICEWORKER = process.env.REACT_APP_ENABLE_SERVICEWORKER || 0; export const MAX_AOI_AREA = Number(process.env.REACT_APP_MAX_AOI_AREA) || 5000; -export const MAX_FILESIZE = parseInt(process.env.REACT_APP_MAX_FILESIZE) || 1000000; // bytes +export const MAX_FILESIZE = Number.parseInt(process.env.REACT_APP_MAX_FILESIZE) || 1000000; // bytes // ORGANISATIONAL INFORMATION export const ORG_NAME = process.env.REACT_APP_ORG_NAME || 'Humanitarian OpenStreetMap Team'; @@ -164,11 +163,9 @@ const bingStyle = { // Removed Mapbox-specific basemap options ('bright-v9', 'satellite-v9') // since we're fully migrated to MapLibre, which does not support mapbox:// styles. export const BASEMAP_OPTIONS = [ - // { label: 'default', value: 'bright-v9' }, { label: 'Default', value: fallbackRasterStyle }, // { label: 'density', value: wmsDensityStyle }, { label: 'bing', value: bingStyle }, - // { label: 'mapbox satellite', value: 'satellite-v9' }, ]; // Removed Mapbox style conditional since we're now using MapLibre only. diff --git a/frontend/src/hooks/UseMapboxSupportedLanguage.js b/frontend/src/hooks/UseMapboxSupportedLanguage.js deleted file mode 100644 index 577fb6407a..0000000000 --- a/frontend/src/hooks/UseMapboxSupportedLanguage.js +++ /dev/null @@ -1,23 +0,0 @@ -import { useSelector } from 'react-redux'; -import MapboxLanguage from '@mapbox/mapbox-gl-language'; - -const { supportedLanguages } = new MapboxLanguage(); - -const defaultLocale = 'en'; - -/** - * A React custom hook to check if the locale language is supported by Mapbox GL or not - * - * Returns `en` if the locale is not supported, else returns preferred locale - * - */ -export default function useMapboxSupportedLanguage() { - const locale = useSelector((state) => state.preferences.locale); - - if (!locale) return defaultLocale; - - const language = locale.substr(0, 2); - if (supportedLanguages.includes(language)) return language; - - return defaultLocale; -} diff --git a/frontend/src/hooks/UseProjectCompletenessCalc.js b/frontend/src/hooks/UseProjectCompletenessCalc.js index 933344230b..cbfa7cfd40 100644 --- a/frontend/src/hooks/UseProjectCompletenessCalc.js +++ b/frontend/src/hooks/UseProjectCompletenessCalc.js @@ -12,9 +12,9 @@ export function useComputeCompleteness(tasks) { ).length; const validated = getStatusCount(tasks, 'VALIDATED'); const badImagery = getStatusCount(tasks, 'BADIMAGERY'); - setPercentMapped(parseInt(((mapped + validated) / (totalTasks - badImagery)) * 100)); - setPercentValidated(parseInt((validated / (totalTasks - badImagery)) * 100)); - setPercentBadImagery(parseInt((badImagery / totalTasks) * 100)); + setPercentMapped(Number.parseInt(((mapped + validated) / (totalTasks - badImagery)) * 100)); + setPercentValidated(Number.parseInt((validated / (totalTasks - badImagery)) * 100)); + setPercentBadImagery(Number.parseInt((badImagery / totalTasks) * 100)); } }, [tasks, setPercentMapped, setPercentValidated, setPercentBadImagery]); return { percentMapped, percentValidated, percentBadImagery }; diff --git a/frontend/src/utils/taskGrid.js b/frontend/src/utils/taskGrid.js index 2ba98ab7cb..e8b632ec66 100644 --- a/frontend/src/utils/taskGrid.js +++ b/frontend/src/utils/taskGrid.js @@ -56,10 +56,10 @@ export const createTaskGrid = (areaOfInterestExtent, zoomLevel) => { const step = AXIS_OFFSET / Math.pow(2, zoomLevel - 1); // Calculate the min and max task indices at the required zoom level to cover the whole area of interest - const xminstep = parseInt(Math.floor((xmin + AXIS_OFFSET) / step)); - const xmaxstep = parseInt(Math.ceil((xmax + AXIS_OFFSET) / step)); - const yminstep = parseInt(Math.floor((ymin + AXIS_OFFSET) / step)); - const ymaxstep = parseInt(Math.ceil((ymax + AXIS_OFFSET) / step)); + const xminstep = Number.parseInt(Math.floor((xmin + AXIS_OFFSET) / step)); + const xmaxstep = Number.parseInt(Math.ceil((xmax + AXIS_OFFSET) / step)); + const yminstep = Number.parseInt(Math.floor((ymin + AXIS_OFFSET) / step)); + const ymaxstep = Number.parseInt(Math.ceil((ymax + AXIS_OFFSET) / step)); let taskFeatures = []; // Generate an array of task features diff --git a/frontend/src/utils/terrawDraw.js b/frontend/src/utils/terrawDraw.js new file mode 100644 index 0000000000..523497f5d2 --- /dev/null +++ b/frontend/src/utils/terrawDraw.js @@ -0,0 +1,15 @@ +function getAllFeatures(drawInstance) { + if (!drawInstance) return; + return drawInstance.getSnapshot().reduce((previous, current) => { + if (current.properties.mode !== 'select') return [...previous, { ...current, properties: {} }]; + return previous; + }, []); +} + +function removeFeaturesById(drawInstance, idList) { + if (!drawInstance || !idList || !idList?.length) return; + const currentfeatureIds = getAllFeatures(drawInstance).map((f) => f.id); + const deletableIds = idList.filter((id) => currentfeatureIds.includes(id)); + drawInstance.removeFeatures(deletableIds); +} +export { getAllFeatures, removeFeaturesById }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 5306cc0ec1..58242b6200 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2030,11 +2030,6 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== -"@mapbox/extent@0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@mapbox/extent/-/extent-0.4.0.tgz#3e591f32e1f0c3981c864239f7b0ac06e610f8a9" - integrity sha512-MSoKw3qPceGuupn04sdaJrFeLKvcSETVLZCGS8JA9x6zXQL3FWiKaIXYIZEDXd5jpXpWlRxinCZIN49yRy0C9A== - "@mapbox/geojson-area@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz#18d7814aa36bf23fbbcc379f8e26a22927debf10" @@ -2042,29 +2037,6 @@ dependencies: wgs84 "0.0.0" -"@mapbox/geojson-coords@0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@mapbox/geojson-coords/-/geojson-coords-0.0.2.tgz#f73d5744c832de0f05c48899f16a4288cefb2606" - integrity sha512-YuVzpseee/P1T5BWyeVVPppyfmuXYHFwZHmybkqaMfu4BWlOf2cmMGKj2Rr92MwfSTOCSUA0PAsVGRG8akY0rg== - dependencies: - "@mapbox/geojson-normalize" "0.0.1" - geojson-flatten "^1.0.4" - -"@mapbox/geojson-extent@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@mapbox/geojson-extent/-/geojson-extent-1.0.1.tgz#bd99a6b66ba98e63a29511c9cd1bbd1df4c1e203" - integrity sha512-hh8LEO3djT4fqfr8sSC6wKt+p0TMiu+KOLMBUiFOyj+zGq7+IXwQGl0ppCVDkyzCewyd9LoGe9zAvDxXrLfhLw== - dependencies: - "@mapbox/extent" "0.4.0" - "@mapbox/geojson-coords" "0.0.2" - rw "~0.1.4" - traverse "~0.6.6" - -"@mapbox/geojson-normalize@0.0.1", "@mapbox/geojson-normalize@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@mapbox/geojson-normalize/-/geojson-normalize-0.0.1.tgz#1da1e6b3a7add3ad29909b30f438f60581b7cd80" - integrity sha512-82V7YHcle8lhgIGqEWwtXYN5cy0QM/OHq3ypGhQTbvHR57DF0vMHMjjVSQKFfVXBe/yWCBZTyOuzvK7DFFnx5Q== - "@mapbox/geojson-rewind@0.5.2", "@mapbox/geojson-rewind@^0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a" @@ -2078,24 +2050,6 @@ resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ== -"@mapbox/mapbox-gl-draw@^1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-draw/-/mapbox-gl-draw-1.4.3.tgz#3f95362fdeabcffcb46f3392f24bf5914e7be817" - integrity sha512-03qIJgyGmm0IoTZbV/cfODru9jRGogi4LcQ3maxIJDKccq1gY3ofgt2UYPkeU143ElxitZahEythNQv1NpsLhg== - dependencies: - "@mapbox/geojson-area" "^0.2.2" - "@mapbox/geojson-extent" "^1.0.1" - "@mapbox/geojson-normalize" "^0.0.1" - "@mapbox/point-geometry" "^0.1.0" - hat "0.0.3" - lodash.isequal "^4.5.0" - xtend "^4.0.2" - -"@mapbox/mapbox-gl-language@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-language/-/mapbox-gl-language-0.10.1.tgz#44d19d24205a10a9f27ddd78c55446fe48b94705" - integrity sha512-i5+N+FrgR5v8NY4+B+uzFl1Q4gR2OaGoKWgKiayL1bE5715svnx4r4ADTczbZSemI9o426tDKfjAtc3HC2GMCw== - "@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" @@ -3881,6 +3835,11 @@ loupe "^3.1.3" tinyrainbow "^2.0.0" +"@watergis/maplibre-gl-terradraw@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@watergis/maplibre-gl-terradraw/-/maplibre-gl-terradraw-1.9.5.tgz#f4801bd78b576bd59ce05346edcbad8ea1a2eaed" + integrity sha512-zpKUF3XBl6PKBDxEKJ9Ii/m61mkq/2W+04zA97hU0jDP0/S8Opht5Ju30uiA+f8zKBhb9KvLexxK73fVd1y4Og== + "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -7364,11 +7323,6 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -geojson-flatten@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/geojson-flatten/-/geojson-flatten-1.1.1.tgz#601aae07ba6406281ebca683573dcda69eba04c7" - integrity sha512-k/6BCd0qAt7vdqdM1LkLfAy72EsLDy0laNwX0x2h49vfYCiQkRc4PSra8DNEdJ10EKRpwEvDXMb0dBknTJuWpQ== - geojson-numeric@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/geojson-numeric/-/geojson-numeric-0.2.1.tgz#2c8c032f792cca0747a6e02ffa86c00ee3d2df6c" @@ -7816,11 +7770,6 @@ hastscript@^7.0.0: property-information "^6.0.0" space-separated-tokens "^2.0.0" -hat@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a" - integrity sha512-zpImx2GoKXy42fVDSEad2BPKuSQdLcqsCYa48K3zHSzM/ugWuYjLDr8IXxpVuL7uCLHw56eaiLxCRthhOzf5ug== - he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -9512,11 +9461,6 @@ lodash.debounce@^4.0.6, lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -9641,11 +9585,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -mapbox-gl-draw-rectangle-mode@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mapbox-gl-draw-rectangle-mode/-/mapbox-gl-draw-rectangle-mode-1.0.4.tgz#42987d68872a5fb5cc5d76d3375ee20cd8bab8f7" - integrity sha512-BdF6nwEK2p8n9LQoMPzBO8LhddW1fe+d5vK8HQIei+4VcRnUbKNsEj7Z15FsJxCHzsc2BQKXbESx5GaE8x0imQ== - mapillary-js@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/mapillary-js/-/mapillary-js-4.1.2.tgz#ed4df0e0926a6af6f46d28b73181e154cc429b67" @@ -12756,11 +12695,6 @@ rw@1, rw@^1.3.3: resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== -rw@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/rw/-/rw-0.1.4.tgz#4903cbd80248ae0ede685bf58fd236a7a9b29a3e" - integrity sha512-vSj3D96kMcjNyqPcp65wBRIDImGSrUuMxngNNxvw8MQaO+aQ6llzRPH7XcJy5zrpb3wU++045+Uz/IDIM684iw== - rxjs@^7.3.0, rxjs@^7.5.5: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" @@ -13839,6 +13773,11 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" +terra-draw@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/terra-draw/-/terra-draw-1.18.1.tgz#f08c78f5d157846475115e5fa6e6f1d5108d96c8" + integrity sha512-8Bw4jHGdW5lzrlfKNYa2k10dirHs/nIkLRDMLSs8OD7IoZiq59cwNEjQIuXggxIZUTfxVwEPytakcdrmZyLADA== + terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.3.10: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" @@ -14055,15 +13994,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -traverse@~0.6.6: - version "0.6.10" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.10.tgz#4c93482381d794dee046882c036f3c4eee481324" - integrity sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA== - dependencies: - gopd "^1.0.1" - typedarray.prototype.slice "^1.0.3" - which-typed-array "^1.1.15" - tributejs@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.1.3.tgz#980600fc72865be5868893078b4bfde721129eae" @@ -14238,18 +14168,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray.prototype.slice@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz#bce2f685d3279f543239e4d595e0d021731d2d1a" - integrity sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.0" - es-errors "^1.3.0" - typed-array-buffer "^1.0.2" - typed-array-byte-offset "^1.0.2" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -15322,7 +15240,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==