fix: Upgrade map widget library to support react 17 (#19315)

This PR:
 - updates the react 16 to react 17
 - replaces the underlying library for the map widget
 - adds clustering of markers
 - refactor code for map widget's component
 
## Description

Fixes #16946

## Type of change
- Breaking change


## How Has This Been Tested?
- Manually

### Test Plan
https://github.com/appsmithorg/TestSmith/issues/2149

### Issues raised during DP testing
1.
https://github.com/appsmithorg/appsmith/pull/19315#issuecomment-1378495845


## Checklist:
### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


### QA activity:
- [ ] Test plan has been approved by relevant developers
- [x] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test

Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com>
Co-authored-by: Arsalan <arsalanyaldram0211@outlook.com>
Co-authored-by: rahulramesha <rahul@appsmith.com>
This commit is contained in:
Pawan Kumar 2023-01-14 00:19:21 +05:30 committed by GitHub
parent 5231d307b8
commit eba4745965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 641 additions and 452 deletions

View File

@ -50,9 +50,9 @@ describe("Fork a template to the current app", () => {
cy.wait(6000);
cy.get("body").then(($ele) => {
if ($ele.find(widgetLocators.toastAction).length <= 0) {
if ($ele.find(template.templateViewForkButton).length> 0) {
if ($ele.find(template.templateViewForkButton).length > 0) {
cy.get(template.templateViewForkButton).click();
}
}
}
});
cy.get(widgetLocators.toastAction).should(

View File

@ -23,8 +23,8 @@ describe("Fork a template to the current app from new page popover", () => {
cy.wait(6000);
cy.get("body").then(($ele) => {
if ($ele.find(widgetLocators.toastAction).length <= 0) {
if ($ele.find(template.templateViewForkButton).length> 0) {
cy.get(template.templateViewForkButton).click();
if ($ele.find(template.templateViewForkButton).length > 0) {
cy.get(template.templateViewForkButton).click();
}
}
});

View File

@ -37,9 +37,9 @@ describe("Fork a template to the current app", () => {
cy.wait(6000);
cy.get("body").then(($ele) => {
if ($ele.find(widgetLocators.toastAction).length <= 0) {
if ($ele.find(template.templateViewForkButton).length> 0) {
if ($ele.find(template.templateViewForkButton).length > 0) {
cy.get(template.templateViewForkButton).click();
}
}
}
});
cy.get(widgetLocators.toastAction).should(

View File

@ -17,10 +17,13 @@
"@draft-js-plugins/mention": "^4.5.1",
"@fusioncharts/powercharts": "^3.16.0",
"@github/g-emoji-element": "^1.1.5",
"@googlemaps/markerclusterer": "^2.0.14",
"@googlemaps/react-wrapper": "^1.1.35",
"@manaflair/redux-batch": "^1.0.0",
"@sentry/react": "^6.2.4",
"@sentry/tracing": "^6.2.4",
"@tinymce/tinymce-react": "^3.13.0",
"@types/google.maps": "^3.51.0",
"@types/react-page-visibility": "^6.4.1",
"@uppy/core": "^1.16.0",
"@uppy/dashboard": "^1.16.0",
@ -46,7 +49,7 @@
"cypress-log-to-output": "^1.1.2",
"dayjs": "^1.10.6",
"deep-diff": "^1.0.2",
"design-system": "npm:@appsmithorg/design-system@1.0.45",
"design-system": "npm:@appsmithorg/design-system@1.0.46",
"downloadjs": "^1.4.7",
"draft-js": "^0.11.7",
"exceljs-lightweight": "^1.14.0",
@ -97,7 +100,7 @@
"rc-select": "^14.1.9",
"rc-tree-select": "^5.4.0",
"re-reselect": "^3.4.0",
"react": "^16.12.0",
"react": "^17.0.2",
"react-beautiful-dnd": "^12.2.0",
"react-custom-scrollbars": "^4.2.1",
"react-device-detect": "^2.2.2",
@ -105,10 +108,9 @@
"react-dnd-html5-backend": "^9.3.4",
"react-dnd-touch-backend": "^9.4.0",
"react-documents": "^1.0.4",
"react-dom": "^16.7.0",
"react-dom": "^17.0.2",
"react-full-screen": "^1.1.0",
"react-fusioncharts": "^3.1.2",
"react-google-maps": "^9.4.5",
"react-google-recaptcha": "^2.1.0",
"react-helmet": "^5.2.1",
"react-hook-form": "^7.28.0",
@ -212,10 +214,10 @@
"@types/node-forge": "^0.10.0",
"@types/papaparse": "^5.3.5",
"@types/prismjs": "^1.16.1",
"@types/react": "^16.8.2",
"@types/react": "^17.0.2",
"@types/react-beautiful-dnd": "^11.0.4",
"@types/react-custom-scrollbars": "^4.0.7",
"@types/react-dom": "^16.8.0",
"@types/react-dom": "^17.0.2",
"@types/react-google-recaptcha": "^2.1.1",
"@types/react-helmet": "^5.0.14",
"@types/react-instantsearch-dom": "^6.3.0",

View File

@ -1,18 +1,30 @@
import React, { useState } from "react";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
import SearchBox from "react-google-maps/lib/components/places/SearchBox";
import StandaloneSearchBox from "react-google-maps/lib/components/places/StandaloneSearchBox";
import { getAppsmithConfigs } from "@appsmith/configs";
import { useScript, ScriptStatus, AddScriptTo } from "utils/hooks/useScript";
import { StyledInputGroup } from "./StyledControls";
import log from "loglevel";
import React, { useState } from "react";
import styled from "styled-components";
import { Wrapper, Status } from "@googlemaps/react-wrapper";
import { StyledInputGroup } from "./StyledControls";
import { getAppsmithConfigs } from "@appsmith/configs";
import { isDynamicValue } from "utils/DynamicBindingUtils";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
const { google } = getAppsmithConfigs();
class LocationSearchControl extends BaseControl<ControlProps> {
searchBox: any = null;
const MapStatusText = styled.span`
font-size: 14px;
`;
const renderMapStatus = (status: Status) => {
switch (status) {
case Status.LOADING:
return <MapStatusText>Loading...</MapStatusText>;
case Status.FAILURE:
return <MapStatusText>Error in the component</MapStatusText>;
case Status.SUCCESS:
return <MapStatusText>Component loaded....</MapStatusText>;
}
};
class LocationSearchControl extends BaseControl<ControlProps> {
clearLocation = () => {
this.updateProperty(this.props.propertyName, {
lat: -34.397,
@ -21,10 +33,10 @@ class LocationSearchControl extends BaseControl<ControlProps> {
});
};
onLocationSelection = () => {
onLocationSelection = (ref: any) => {
try {
// For some places, the length is zero
const places = this.searchBox.getPlaces();
const places = ref.getPlaces();
const location = places[0].geometry.location;
const title = places[0].formatted_address;
const lat = location.lat();
@ -32,26 +44,36 @@ class LocationSearchControl extends BaseControl<ControlProps> {
const value = { lat, long, title };
this.updateProperty(this.props.propertyName, value, true);
} catch (e) {
if (this.searchBox && this.searchBox.getPlaces)
log.debug("Error selecting location:", this.searchBox.getPlaces());
if (ref && ref.getPlaces)
log.debug("Error selecting location:", ref.getPlaces());
else {
log.debug("Error selecting location - searchBox not found");
}
}
};
onSearchBoxMounted = (ref: SearchBox) => {
this.searchBox = ref;
onSearchBoxMounted = (ref: any) => {
if (window) {
const searchBox = new window.google.maps.places.SearchBox(ref);
searchBox.addListener("places_changed", () => {
this.onLocationSelection(searchBox);
});
}
};
render() {
return (
<MapScriptWrapper
clearLocation={this.clearLocation}
onPlacesChanged={this.onLocationSelection}
onSearchBoxMounted={this.onSearchBoxMounted}
propertyValue={this.props.propertyValue}
/>
<Wrapper
apiKey={google.apiKey}
libraries={["geometry", "drawing", "places"]}
render={renderMapStatus}
>
<MapScriptWrapper
clearLocation={this.clearLocation}
onSearchBoxMounted={this.onSearchBoxMounted}
propertyValue={this.props.propertyValue}
/>
</Wrapper>
);
}
@ -65,43 +87,29 @@ class LocationSearchControl extends BaseControl<ControlProps> {
}
interface MapScriptWrapperProps {
onSearchBoxMounted: (ref: SearchBox) => void;
onPlacesChanged: () => void;
onSearchBoxMounted: (ref: any) => void;
clearLocation: () => void;
propertyValue: any;
}
function MapScriptWrapper(props: MapScriptWrapperProps) {
const status = useScript(
`https://maps.googleapis.com/maps/api/js?key=${google.apiKey}&v=3.exp&libraries=geometry,drawing,places`,
AddScriptTo.HEAD,
);
const [title, setTitle] = useState("");
return (
<div data-standalone-searchbox="">
{status === ScriptStatus.READY && (
<StandaloneSearchBox
onPlacesChanged={() => {
props.onPlacesChanged();
setTitle("");
}}
ref={props.onSearchBoxMounted}
>
<StyledInputGroup
dataType="text"
defaultValue={title || props.propertyValue?.title}
onChange={(value: string) => {
if (value === "") {
props.clearLocation();
}
setTitle(value);
}}
placeholder="Enter location"
tabIndex={-1}
/>
</StandaloneSearchBox>
)}
<StyledInputGroup
dataType="text"
defaultValue={title || props.propertyValue?.title}
onChange={(value: string) => {
if (value === "") {
props.clearLocation();
}
setTitle(value);
}}
placeholder="Enter location"
ref={props.onSearchBoxMounted}
tabIndex={-1}
/>
</div>
);
}

View File

@ -43,17 +43,19 @@ export function PropertyPaneSearchInput(props: PropertyPaneSearchInputProps) {
useEffect(() => {
// Checks if the property pane opened not because of focusing an input inside a widget
const isActiveFocusNotFromWidgetInput = !isCurrentFocusOnInput();
if (
shouldFocusSearch &&
isActiveFocusNotFromWidgetInput &&
// while the panel transition happens, focus will be happening twice. Once on the main pane and then on the panel
// The following check will make sure that the focus is only done once and prevents the UI jittering
isPanel === shouldFocusPanelSearch
) {
setTimeout(
() => {
wrapperRef.current?.focus();
//checking for active element
//inside timeout to have updated active element
if (!isCurrentFocusOnInput()) {
wrapperRef.current?.focus();
}
},
// Layered panels like Column Panel's transition takes 300ms.
// To avoid UI jittering, we are delaying the focus by 300ms.

View File

@ -1,15 +1,15 @@
import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors";
import { useSelector } from "react-redux";
import { AppState } from "@appsmith/reducers";
import { useWidgetSelection } from "./useWidgetSelection";
import equal from "fast-deep-equal/es6";
import React, { ReactNode, useCallback } from "react";
import { stopEventPropagation } from "utils/AppsmithUtils";
import { useSelector } from "react-redux";
import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors";
import {
getFocusedParentToOpen,
isWidgetSelected,
shouldWidgetIgnoreClicksSelector,
} from "selectors/widgetSelectors";
import equal from "fast-deep-equal/es6";
import { stopEventPropagation } from "utils/AppsmithUtils";
import { useWidgetSelection } from "./useWidgetSelection";
export function ClickContentToOpenPropPane({
children,
@ -49,7 +49,7 @@ export function ClickContentToOpenPropPane({
return (
<div
onClick={stopEventPropagation}
onClickCapture={clickToSelectWidget}
onMouseDownCapture={clickToSelectWidget}
onMouseOver={handleMouseOver}
style={styles}
>

View File

@ -0,0 +1,88 @@
import React, { useEffect, useState } from "react";
import { MarkerClusterer } from "@googlemaps/markerclusterer";
import Marker from "./Marker";
import { MapComponentProps } from ".";
type ClustererProps = {
map?: google.maps.Map;
} & Pick<
MapComponentProps,
| "selectMarker"
| "updateCenter"
| "updateMarker"
| "markers"
| "selectedMarker"
| "clickedMarkerCentered"
>;
const Clusterer: React.FC<ClustererProps> = (props) => {
const {
clickedMarkerCentered,
map,
markers,
selectedMarker,
selectMarker,
updateCenter,
updateMarker,
} = props;
const [markerClusterer, setMarkerClusterer] = useState<MarkerClusterer>();
// add marker clusterer on map
useEffect(() => {
if (!map) return;
setMarkerClusterer(
new MarkerClusterer({
map,
}),
);
return () => {
if (markerClusterer) {
markerClusterer.clearMarkers();
}
};
}, [map]);
if (!Array.isArray(markers)) return null;
return (
<>
{markers.map((marker, index) => (
<Marker
clickable
draggable={
selectedMarker &&
selectedMarker?.lat === marker.lat &&
selectedMarker?.long === marker.long
}
key={index}
map={map}
markerClusterer={markerClusterer}
onClick={() => {
if (clickedMarkerCentered) {
updateCenter(marker.lat, marker.long);
}
selectMarker(marker.lat, marker.long, marker.title);
}}
onDragEnd={(e) => {
updateMarker(
Number(e.latLng?.lat()),
Number(e.latLng?.lng()),
index,
);
}}
position={{
lat: marker.lat,
lng: marker.long,
}}
title={marker.title}
/>
))}
</>
);
};
export default Clusterer;

View File

@ -0,0 +1,143 @@
import styled from "styled-components";
import React, { useCallback, useEffect, useRef, useState } from "react";
import Clusterer from "./Clusterer";
import SearchBox from "./SearchBox";
import { MapComponentProps } from ".";
import PickMyLocation from "./PickMyLocation";
const Wrapper = styled.div`
position: relative;
`;
const StyledMap = styled.div<Pick<MapProps, "borderRadius" | "boxShadow">>`
height: 100%;
width: 100%;
border-radius: ${(props) => props.borderRadius};
box-shadow: ${(props) => props.boxShadow};
`;
type MapProps = {
children?: React.ReactNode;
} & Pick<
MapComponentProps,
| "updateCenter"
| "zoomLevel"
| "updateMarker"
| "selectMarker"
| "saveMarker"
| "markers"
| "center"
| "enableCreateMarker"
| "selectedMarker"
| "borderRadius"
| "boxShadow"
| "clickedMarkerCentered"
| "enableDrag"
>;
const Map = (props: MapProps) => {
const {
borderRadius,
boxShadow,
center,
children,
enableCreateMarker,
enableDrag,
markers,
saveMarker,
selectMarker,
updateCenter,
updateMarker,
zoomLevel,
} = props;
const [map, setMap] = useState<google.maps.Map>();
const mapRef = useRef<HTMLDivElement>(null);
// initialize the map instance
useEffect(() => {
if (!mapRef.current) return;
setMap(
new window.google.maps.Map(mapRef.current, {
streetViewControl: false,
mapTypeControl: false,
fullscreenControl: false,
}),
);
}, [mapRef]);
// set center if center is changed
useEffect(() => {
if (map) {
map.setCenter({ lat: center.lat, lng: center.long });
}
}, [center, map]);
// set zoom if zoomLevel is changed
useEffect(() => {
if (map) {
map.setZoom(Math.floor(zoomLevel / 5));
}
}, [zoomLevel, map]);
/**
* on click map, add marker
*
* @param e
* @returns
*/
const onClickOnMap = useCallback(
(e: google.maps.MapMouseEvent) => {
if (!enableCreateMarker || !saveMarker) return;
// only save marker when lag and long are available
if (e.latLng?.lat() && e.latLng?.lng()) {
saveMarker(Number(e.latLng.lat()), Number(e.latLng.lng()));
}
},
[enableCreateMarker, saveMarker],
);
// NOTE: The event listeners require code to clear existing listeners
// when a handler passed as a prop has been updated.
React.useEffect(() => {
if (map) {
["click", "idle"].forEach((eventName) =>
google.maps.event.clearListeners(map, eventName),
);
map.addListener("click", onClickOnMap);
}
}, [map, onClickOnMap]);
return (
<Wrapper onMouseLeave={enableDrag}>
<StyledMap
borderRadius={borderRadius}
boxShadow={boxShadow}
id="map"
ref={mapRef}
/>
<Clusterer
map={map}
markers={markers}
selectMarker={selectMarker}
updateCenter={updateCenter}
updateMarker={updateMarker}
/>
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child as React.ReactElement<any>, {
map,
});
}
})}
</Wrapper>
);
};
Map.PickMyLocation = PickMyLocation;
Map.SearchBox = SearchBox;
export default Map;

View File

@ -0,0 +1,56 @@
import React, { useEffect, useState } from "react";
import { MarkerClusterer } from "@googlemaps/markerclusterer";
type MarkerProps = google.maps.MarkerOptions & {
onClick?: () => void;
map?: google.maps.Map;
markerClusterer?: MarkerClusterer;
onDragEnd?: (e: google.maps.MapMouseEvent) => void;
};
const Marker: React.FC<MarkerProps> = (options) => {
const { markerClusterer, onClick, onDragEnd, position, ...rest } = options;
const [marker, setMarker] = useState<google.maps.Marker>();
useEffect(() => {
if (!marker) {
const marker = new google.maps.Marker({ position });
marker.addListener("click", () => {
if (onClick) onClick();
});
marker.setOptions(rest);
setMarker(marker);
}
if (markerClusterer && marker) {
markerClusterer.addMarker(marker);
}
// remove marker from map on unmount
return () => {
if (marker) {
marker.setMap(null);
}
if (markerClusterer && marker) {
markerClusterer.removeMarker(marker);
}
};
}, [marker, markerClusterer]);
// add dragend event on marker
useEffect(() => {
if (!marker) return;
marker.addListener("dragend", (e: google.maps.MapMouseEvent) => {
if (onDragEnd) onDragEnd(e);
});
}, [marker, options.onDragEnd]);
return null;
};
export default Marker;

View File

@ -1,17 +1,11 @@
import React from "react";
import { ControlIcons } from "icons/ControlIcons";
import styled from "styled-components";
const StyledPickMyLocationSelectedIcon = styled(
ControlIcons.PICK_MY_LOCATION_SELECTED_CONTROL,
)`
position: relative;
cursor: pointer;
height: 28px;
width: 28px;
`;
import { ControlIcons } from "icons/ControlIcons";
const PickMyLocationIconWrapper = styled.div`
const Icon = styled(ControlIcons.PICK_MY_LOCATION_SELECTED_CONTROL)<{
allowZoom?: boolean;
}>`
background: white;
width: 40px;
height: 40px;
@ -20,57 +14,55 @@ const PickMyLocationIconWrapper = styled.div`
justify-content: center;
align-items: center;
box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px;
right: 10px;
bottom: ${(props) => (props.allowZoom ? "110px" : "20px")};
position: absolute;
`;
interface PickMyLocationProps {
title?: string;
isEnabled?: boolean;
allowZoom?: boolean;
updateCenter: (lat: number, long: number) => void;
}
interface PickMyLocationState {
clicked: boolean;
selected: boolean;
}
export default class PickMyLocation extends React.Component<
PickMyLocationProps,
PickMyLocationState
> {
constructor(props: PickMyLocationProps) {
super(props);
this.state = {
selected: false,
clicked: false,
};
}
getUserLocation = () => {
const PickMyLocation: React.FC<PickMyLocationProps> = (props) => {
const { allowZoom, isEnabled, title, updateCenter } = props;
const [clicked, setClicked] = React.useState(false);
const [selected, setSelected] = React.useState(false);
/**
* get user location
*
* @returns
*/
const getUserLocation = () => {
if ("geolocation" in navigator) {
return navigator.geolocation.getCurrentPosition((data) => {
const {
coords: { latitude: lat, longitude: long },
} = data;
this.setState({ selected: true });
this.props.updateCenter(lat, long);
setSelected(true);
updateCenter(lat, long);
});
}
};
render() {
return (
<PickMyLocationIconWrapper>
<StyledPickMyLocationSelectedIcon
color={
this.state.selected
? "#049ADA"
: this.state.clicked
? "#666666"
: "#999999"
}
onClick={() => {
if (!(this.state.clicked && this.state.selected)) {
this.setState({ clicked: true });
this.getUserLocation();
}
}}
/>
</PickMyLocationIconWrapper>
);
}
}
if (!isEnabled) return null;
return (
<Icon
allowZoom={allowZoom}
color={selected ? "#049ADA" : clicked ? "#666666" : "#999999"}
onClick={() => {
if (!(clicked && selected)) {
setClicked(true);
getUserLocation();
}
}}
title={title}
/>
);
};
export default PickMyLocation;

View File

@ -0,0 +1,72 @@
import styled from "styled-components";
import React, { useRef, useEffect } from "react";
type Places = google.maps.places.PlaceResult[] | undefined;
type SearchBoxProps = {
isEnabled: boolean;
map?: google.maps.Map;
placeholder?: string;
updateCenter: (lat: number, long: number) => void;
};
const StyledInput = styled.input`
position: absolute;
top: 0%;
box-sizing: border-box;
border: 1px solid transparent;
width: min(90%, 240px);
height: 32px;
padding: 0 12px;
border-radius: 3px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
font-size: 14px;
outline: none;
text-overflow: ellipses;
left: 0;
right: 0;
margin: 24px auto;
`;
const SearchBox = (props: SearchBoxProps) => {
const { isEnabled, updateCenter } = props;
const searchBoxRef = useRef<HTMLInputElement>(null);
const searchBoxObjRef = useRef<google.maps.places.SearchBox>();
// initialize search box
useEffect(() => {
if (!searchBoxRef.current) return;
searchBoxObjRef.current = new window.google.maps.places.SearchBox(
searchBoxRef.current,
);
}, [searchBoxRef]);
// add event listeners to search box
useEffect(() => {
if (!searchBoxObjRef.current) return;
searchBoxObjRef.current?.addListener("places_changed", () => {
const places: Places = searchBoxObjRef.current?.getPlaces();
const location = places ? places[0].geometry?.location : undefined;
if (location) {
const lat = location.lat();
const long = location.lng();
updateCenter(lat, long);
}
});
}, [updateCenter]);
if (!isEnabled) return null;
return (
<StyledInput
placeholder="Enter location to search"
ref={searchBoxRef}
type="text"
/>
);
};
export default SearchBox;

View File

@ -1,13 +1,17 @@
import React, { useEffect, useMemo } from "react";
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import SearchBox from "react-google-maps/lib/components/places/SearchBox";
import { MarkerProps } from "../constants";
import PickMyLocation from "./PickMyLocation";
import React from "react";
import styled from "styled-components";
import { useScript, ScriptStatus, AddScriptTo } from "utils/hooks/useScript";
import { Colors } from "constants/Colors";
import { Wrapper, Status } from "@googlemaps/react-wrapper";
interface MapComponentProps {
import Map from "./Map";
export interface MarkerProps {
lat: number;
long: number;
title?: string;
description?: string;
color?: string;
}
export interface MapComponentProps {
apiKey: string;
widgetId: string;
isDisabled?: boolean;
@ -20,7 +24,7 @@ interface MapComponentProps {
lat: number;
long: number;
};
markers?: Array<MarkerProps>;
markers?: MarkerProps[];
selectedMarker?: {
lat: number;
long: number;
@ -28,211 +32,85 @@ interface MapComponentProps {
};
enableCreateMarker: boolean;
clickedMarkerCentered?: boolean;
updateCenter: (lat: number, long: number) => void;
updateCenter: (lat?: number, long?: number) => void;
updateMarker: (lat: number, long: number, index: number) => void;
saveMarker: (lat: number, long: number) => void;
selectMarker: (lat: number, long: number, title: string) => void;
saveMarker: (lat?: number, long?: number) => void;
selectMarker: (lat?: number, long?: number, title?: string) => void;
enableDrag: (e: any) => void;
unselectMarker: () => void;
borderRadius: string;
boxShadow?: string;
}
const MapContainerWrapper = styled.div`
width: 100%;
height: 100%;
`;
const MapWrapper = styled.div<{
borderRadius: string;
boxShadow?: string;
}>`
position: relative;
width: 100%;
height: 100%;
border-radius: ${({ borderRadius }) => borderRadius};
border: ${({ boxShadow }) =>
boxShadow === "none" ? `1px solid` : `0px solid`};
border-color: ${Colors.GREY_3};
overflow: hidden;
box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important;
${({ borderRadius }) =>
borderRadius >= "1.5rem"
? `& div.gmnoprint:not([data-control-width]) {
margin-right: 10px !important;`
: ""}
`;
const StyledInput = styled.input`
box-sizing: border-box;
border: 1px solid transparent;
width: 240px;
height: 32px;
margin-top: 27px;
padding: 0 12px;
border-radius: 3px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
const MapStatusText = styled.span`
font-size: 14px;
outline: none;
text-overflow: ellipses;
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
color: var(--wds-color-text-light);
`;
type PickMyLocationProps = {
allowZoom: boolean;
const MapStatusLoading = styled.span`
font-size: 14px;
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
color: var(--wds-color-text-light);
background: var(--wds-color-bg-light);
text-align: center;
`;
/**
* This component will render the map based on the status of the google maps api.
*
* @param status
* @returns
*/
const renderMapStatus = (status: Status) => {
switch (status) {
case Status.LOADING:
return <MapStatusLoading />;
case Status.FAILURE:
return <MapStatusText>Error while initializing the map</MapStatusText>;
case Status.SUCCESS:
return <div />;
}
};
const PickMyLocationWrapper = styled.div<PickMyLocationProps>`
position: absolute;
bottom: ${(props) => (props.allowZoom ? 110 : 20)}px;
right: -90px;
width: 140px;
`;
const MapComponent = (props: MapComponentProps) => {
const {
allowZoom,
apiKey,
enablePickLocation,
enableSearch,
updateCenter,
...rest
} = props;
const MyMapComponent = withGoogleMap((props: any) => {
const [mapCenter, setMapCenter] = React.useState<
| {
lat: number;
lng: number;
title?: string;
description?: string;
}
| undefined
>({
...props.center,
lng: props.center.long,
});
const searchBox = React.createRef<SearchBox>();
const onPlacesChanged = () => {
const node: any = searchBox.current;
if (node) {
const places: any = node.getPlaces();
if (
places &&
places.length &&
places[0].geometry &&
places[0].geometry.location
) {
const location = places[0].geometry.location;
const lat = location.lat();
const long = location.lng();
setMapCenter({ lat, lng: long });
props.updateCenter(lat, long, places[0].formatted_address);
props.unselectMarker();
}
}
};
useEffect(() => {
if (!props.selectedMarker) {
setMapCenter({
...props.center,
lng: props.center.long,
});
}
}, [props.center, props.selectedMarker]);
return (
<GoogleMap
center={mapCenter}
onClick={(e) => {
if (props.enableCreateMarker) {
props.saveMarker(e.latLng.lat(), e.latLng.lng());
}
}}
options={{
zoomControl: props.allowZoom,
fullscreenControl: false,
mapTypeControl: false,
scrollwheel: false,
rotateControl: false,
streetViewControl: false,
}}
zoom={props.zoom}
<Wrapper
apiKey={apiKey}
libraries={["geometry", "drawing", "places"]}
render={renderMapStatus}
>
{props.enableSearch && (
<SearchBox
controlPosition={2}
onPlacesChanged={onPlacesChanged}
ref={searchBox}
>
<StyledInput placeholder="Enter location to search" type="text" />
</SearchBox>
)}
{Array.isArray(props.markers) &&
props.markers.map((marker: MarkerProps, index: number) => (
<Marker
clickable
draggable={
props.selectedMarker &&
props.selectedMarker.lat === marker.lat &&
props.selectedMarker.long === marker.long
}
icon={{
path:
"M12 23.728L5.636 17.364C4.37734 16.1054 3.52019 14.5017 3.17293 12.7559C2.82567 11.0101 3.00391 9.20047 3.6851 7.55595C4.36629 5.91142 5.51984 4.50582 6.99988 3.51689C8.47992 2.52796 10.22 2.00012 12 2.00012C13.78 2.00012 15.5201 2.52796 17.0001 3.51689C18.4802 4.50582 19.6337 5.91142 20.3149 7.55595C20.9961 9.20047 21.1743 11.0101 20.8271 12.7559C20.4798 14.5017 19.6227 16.1054 18.364 17.364L12 23.728ZM10.5858 12.4143C10.9609 12.7893 11.4696 13 12 13C12.5304 13 13.0391 12.7893 13.4142 12.4143C13.7893 12.0392 14 11.5305 14 11C14 10.4696 13.7893 9.9609 13.4142 9.58583C13.0391 9.21076 12.5304 9.00004 12 9.00004C11.4696 9.00004 10.9609 9.21076 10.5858 9.58583C10.2107 9.9609 10 10.4696 10 11C10 11.5305 10.2107 12.0392 10.5858 12.4143Z",
fillColor: marker.color || "#ea4335",
fillOpacity: 1,
strokeWeight: 0,
scale: 1,
// @ts-expect-error: cannot find name google
anchor: new google.maps.Point(12, 24),
}}
key={index}
onClick={() => {
if (props.clickedMarkerCentered) {
setMapCenter({
...marker,
lng: marker.long,
});
}
props.selectMarker(marker.lat, marker.long, marker.title);
}}
onDragEnd={(de) => {
props.updateMarker(de.latLng.lat(), de.latLng.lng(), index);
}}
position={{ lat: Number(marker.lat), lng: Number(marker.long) }}
title={marker.title}
/>
))}
{props.enablePickLocation && (
<PickMyLocationWrapper
allowZoom={props.allowZoom}
<Map updateCenter={updateCenter} {...rest}>
<Map.PickMyLocation
allowZoom={allowZoom}
isEnabled={enablePickLocation}
title="Pick My Location"
>
<PickMyLocation updateCenter={props.updateCenter} />
</PickMyLocationWrapper>
)}
</GoogleMap>
);
});
function MapComponent(props: MapComponentProps) {
const zoom = Math.floor(props.zoomLevel / 5);
const status = useScript(
`https://maps.googleapis.com/maps/api/js?key=${props.apiKey}&v=3.exp&libraries=geometry,drawing,places`,
AddScriptTo.HEAD,
);
const MapContainerWrapperMemoized = useMemo(() => <MapContainerWrapper />, [
props.borderRadius,
props.boxShadow,
]);
return (
<MapWrapper
borderRadius={props.borderRadius}
boxShadow={props.boxShadow}
onMouseLeave={props.enableDrag}
>
{status === ScriptStatus.READY && (
<MyMapComponent
containerElement={MapContainerWrapperMemoized}
loadingElement={MapContainerWrapperMemoized}
mapElement={MapContainerWrapperMemoized}
{...props}
zoom={zoom}
updateCenter={updateCenter}
/>
)}
</MapWrapper>
<Map.SearchBox
isEnabled={enableSearch}
placeholder="Enter location to search"
updateCenter={updateCenter}
/>
</Map>
</Wrapper>
);
}
};
export default MapComponent;

View File

@ -304,7 +304,7 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
};
}
updateCenter = (lat: number, long: number, title?: string) => {
updateCenter = (lat?: number, long?: number, title?: string) => {
this.props.updateWidgetMetaProperty("center", { lat, long, title });
};
@ -321,7 +321,7 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
this.props.updateWidgetMetaProperty("markers", markers);
};
onCreateMarker = (lat: number, long: number) => {
onCreateMarker = (lat?: number, long?: number) => {
this.disableDrag(true);
const marker = { lat, long, title: "" };
@ -344,7 +344,7 @@ class MapWidget extends BaseWidget<MapWidgetProps, WidgetState> {
this.props.updateWidgetMetaProperty("selectedMarker", undefined);
};
onMarkerClick = (lat: number, long: number, title: string) => {
onMarkerClick = (lat?: number, long?: number, title?: string) => {
this.disableDrag(true);
const selectedMarker = {
lat: lat,

View File

@ -1972,6 +1972,28 @@
version "1.1.5"
resolved "https://registry.npmjs.org/@github/g-emoji-element/-/g-emoji-element-1.1.5.tgz"
"@googlemaps/js-api-loader@^1.13.2":
version "1.15.1"
resolved "https://registry.yarnpkg.com/@googlemaps/js-api-loader/-/js-api-loader-1.15.1.tgz#7d5f1d55a4ec5c99b1d8f0708f3a46b83a71384c"
integrity sha512-AsnEgNsB7S/VdrHGEQUaUM2e5tmjFGKBAfzR/AqO8O7TPq/jQGvoRw5liPBw4EMF38RDsHmKDV89q/X+qiUREQ==
dependencies:
fast-deep-equal "^3.1.3"
"@googlemaps/markerclusterer@^2.0.14":
version "2.0.14"
resolved "https://registry.yarnpkg.com/@googlemaps/markerclusterer/-/markerclusterer-2.0.14.tgz#803dee96260feb26a3e75fb865125f8f3fbd5c29"
integrity sha512-/AKSz4kqMKOGc1ByPK4FWygOi0SHE2MjZdueelx4oShagcR5d9eIz77YMxcyxOUIc/dI0x5/Y0VoUTn+aWEUtQ==
dependencies:
fast-deep-equal "^3.1.3"
supercluster "^7.1.3"
"@googlemaps/react-wrapper@^1.1.35":
version "1.1.35"
resolved "https://registry.yarnpkg.com/@googlemaps/react-wrapper/-/react-wrapper-1.1.35.tgz#fde9146b1ae02805dcad9c0fab56c4bc137b32ee"
integrity sha512-vK+BDQMHN0Oqr66cW3ZPWVK43BUmJJBu6P8T74tc6/fKpUJUlFEaZsupgIIRRRDW9ejB8uGagUmwOnA2gdcvbw==
dependencies:
"@googlemaps/js-api-loader" "^1.13.2"
"@humanwhocodes/config-array@^0.6.0":
version "0.6.0"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz"
@ -3021,6 +3043,11 @@
version "1.1.2"
resolved "https://registry.npmjs.org/@types/fined/-/fined-1.1.2.tgz"
"@types/google.maps@^3.51.0":
version "3.51.0"
resolved "https://registry.yarnpkg.com/@types/google.maps/-/google.maps-3.51.0.tgz#56198ebe9ecb9b83821a290ae2abe97b68e3d20b"
integrity sha512-44/oQYjc5D6kxBcI3Qk9rk3IIOMwnlEMWDV7pwPJ2YI89s5Q1OzDrFvR7QJ3LFrpVXEhig+gyagFg54+foinFg==
"@types/graceful-fs@^4.1.2":
version "4.1.4"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz"
@ -3272,12 +3299,12 @@
dependencies:
"@types/react" "*"
"@types/react-dom@^16.8.0":
version "16.9.16"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.16.tgz#c591f2ed1c6f32e9759dfa6eb4abfd8041f29e39"
integrity sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==
"@types/react-dom@^17.0.2":
version "17.0.18"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.18.tgz#8f7af38f5d9b42f79162eea7492e5a1caff70dc2"
integrity sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==
dependencies:
"@types/react" "^16"
"@types/react" "^17"
"@types/react-google-recaptcha@^2.1.1":
version "2.1.1"
@ -3411,15 +3438,6 @@
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^16", "@types/react@^16.8.2":
version "16.14.28"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.28.tgz#073258f3fe7bb80c748842c1f93aeaafe16dffad"
integrity sha512-83zBE6+XUVXsdL3iFzOyUewdauaU+KviKCHEGOgSW52coAuqW7tEKQM0E9+ZC0Zk6TELQ2/JgogPvp7FavzFwg==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^16.0.40":
version "16.14.8"
resolved "https://registry.npmjs.org/@types/react/-/react-16.14.8.tgz"
@ -3437,6 +3455,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^17", "@types/react@^17.0.2":
version "17.0.52"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b"
integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/redux-form@^8.1.9":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@types/redux-form/-/redux-form-8.3.0.tgz#d253e0078a4940187b946459e0bb4d6a355018b1"
@ -4733,13 +4760,6 @@ babel-preset-react-app@^10.0.1:
babel-plugin-macros "^3.1.0"
babel-plugin-transform-react-remove-prop-types "^0.4.24"
babel-runtime@^6.11.6:
version "6.26.0"
resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
@ -5019,10 +5039,6 @@ camelize@^1.0.0:
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==
can-use-dom@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz"
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz"
@ -5126,10 +5142,6 @@ change-case@^4.1.2:
snake-case "^3.0.4"
tslib "^2.0.3"
change-emitter@^0.1.2:
version "0.1.6"
resolved "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz"
char-regex@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz"
@ -5583,11 +5595,7 @@ core-js-pure@^3.8.1:
resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.5.tgz"
integrity sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA==
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz"
core-js@^2.4.0, core-js@^2.6.5:
core-js@^2.6.5:
version "2.6.11"
resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz"
@ -6259,10 +6267,10 @@ depd@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
"design-system@npm:@appsmithorg/design-system@1.0.45":
version "1.0.45"
resolved "https://registry.yarnpkg.com/@appsmithorg/design-system/-/design-system-1.0.45.tgz#c3156581397beff0b4edc2bbc48ec2f17d51afd4"
integrity sha512-QrrtpQKhR719H6i9cp9OS0zr4lmjwheZcMrInOCllZ8BRr22eT7BFPpRgENzWQqq4deIWWFqD+qFDstImNhPUQ==
"design-system@npm:@appsmithorg/design-system@1.0.46":
version "1.0.46"
resolved "https://registry.yarnpkg.com/@appsmithorg/design-system/-/design-system-1.0.46.tgz#f2091ee536def9cce2f170aed036d62ae4752bb5"
integrity sha512-o1h72Sf0ZX/sUYibhxcPD4s8Z4e/AWRxPO5M45m3+g5sVZHMH25ydEEPBWODxFK2CeaNOU0NP+sBc063Q07NKA==
dependencies:
emoji-mart "3.0.1"
@ -7453,18 +7461,6 @@ fbjs-css-vars@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz"
fbjs@^0.8.1:
version "0.8.17"
resolved "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
fbjs@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz"
@ -8125,10 +8121,6 @@ glur@^1.1.2:
resolved "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz"
integrity sha1-8g6jbbEDv8KSNDkh8fkeg8NGdok=
google-maps-infobox@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/google-maps-infobox/-/google-maps-infobox-2.0.0.tgz"
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz"
@ -8299,10 +8291,6 @@ history@^4.10.1, history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^2.3.1:
version "2.5.5"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
@ -8699,7 +8687,7 @@ interweave@^12.7.2:
dependencies:
escape-html "^1.0.3"
invariant@^2.2.1, invariant@^2.2.4:
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz"
dependencies:
@ -9077,13 +9065,6 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz"
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isomorphic.js@^0.2.4:
version "0.2.4"
resolved "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.4.tgz"
@ -9850,6 +9831,11 @@ jszip@^3.1.3, jszip@^3.1.5, jszip@^3.7.1:
readable-stream "~2.3.6"
set-immediate-shim "~1.0.1"
kdbush@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0"
integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==
kind-of@^6.0.2:
version "6.0.3"
resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
@ -10190,7 +10176,7 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
lodash@4.x, lodash@^4, lodash@^4.16.2, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.21:
lodash@4.x, lodash@^4, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
@ -10340,14 +10326,6 @@ marked@^4.0.18:
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.18.tgz#cd0ac54b2e5610cfb90e8fd46ccaa8292c9ed569"
integrity sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==
marker-clusterer-plus@^2.1.4:
version "2.1.4"
resolved "https://registry.npmjs.org/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz"
markerwithlabel@^2.0.1:
version "2.0.2"
resolved "https://registry.npmjs.org/markerwithlabel/-/markerwithlabel-2.0.2.tgz"
matchmediaquery@^0.3.0:
version "0.3.1"
resolved "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz"
@ -10744,7 +10722,7 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
node-fetch@2.6.7, node-fetch@^1.0.1, node-fetch@^2.6.1, node-fetch@^2.6.7:
node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -12598,15 +12576,14 @@ react-documents@^1.0.4:
resolved "https://registry.npmjs.org/react-documents/-/react-documents-1.0.4.tgz"
integrity sha512-EpoY+MZEu3hPffIWA4FadUYu8daubNkr+LK2zuoPkCAVtyNY+z+/RuzzTriuhjcDydKXzgzp42kQTfAD2j3Mxw==
react-dom@^16.7.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
react-dom@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.19.1"
scheduler "^0.20.2"
react-error-boundary@^3.1.0:
version "3.1.3"
@ -12641,22 +12618,6 @@ react-fusioncharts@^3.1.2:
dependencies:
uuid "^3.2.1"
react-google-maps@^9.4.5:
version "9.4.5"
resolved "https://registry.npmjs.org/react-google-maps/-/react-google-maps-9.4.5.tgz"
dependencies:
babel-runtime "^6.11.6"
can-use-dom "^0.1.0"
google-maps-infobox "^2.0.0"
invariant "^2.2.1"
lodash "^4.16.2"
marker-clusterer-plus "^2.1.4"
markerwithlabel "^2.0.1"
prop-types "^15.5.8"
recompose "^0.26.0"
scriptjs "^2.5.8"
warning "^3.0.0"
react-google-recaptcha@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz"
@ -13061,14 +13022,13 @@ react-zoom-pan-pinch@^1.6.1:
version "1.6.1"
resolved "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-1.6.1.tgz"
react@^16.12.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
react@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
read-pkg@^5.2.0:
version "5.2.0"
@ -13124,15 +13084,6 @@ rechoir@^0.8.0:
dependencies:
resolve "^1.20.0"
recompose@^0.26.0:
version "0.26.0"
resolved "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz"
dependencies:
change-emitter "^0.1.2"
fbjs "^0.8.1"
hoist-non-react-statics "^2.3.1"
symbol-observable "^1.0.4"
recursive-readdir@^2.2.2:
version "2.2.2"
resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz"
@ -13231,10 +13182,6 @@ regenerate@^1.4.2:
resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
@ -13605,6 +13552,14 @@ scheduler@^0.19.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@2.7.0:
version "2.7.0"
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz"
@ -13649,10 +13604,6 @@ schema-utils@^4.0.0:
ajv-formats "^2.1.1"
ajv-keywords "^5.0.0"
scriptjs@^2.5.8:
version "2.5.9"
resolved "https://registry.npmjs.org/scriptjs/-/scriptjs-2.5.9.tgz"
scroll-into-view-if-needed@^2.2.26:
version "2.2.26"
resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.26.tgz"
@ -14342,6 +14293,13 @@ stylehacks@^5.1.0:
browserslist "^4.16.6"
postcss-selector-parser "^6.0.4"
supercluster@^7.1.3:
version "7.1.5"
resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3"
integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==
dependencies:
kdbush "^3.0.0"
supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1:
version "8.1.1"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
@ -14412,7 +14370,7 @@ svgo@^2.7.0:
picocolors "^1.0.0"
stable "^0.1.8"
symbol-observable@^1.0.4, symbol-observable@^1.2.0:
symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz"
@ -15192,12 +15150,6 @@ walker@^1.0.7:
dependencies:
makeerror "1.0.x"
warning@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz"
dependencies:
loose-envify "^1.0.0"
warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz"
@ -15403,10 +15355,6 @@ whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@>=0.10.0:
version "3.4.1"
resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz"
whatwg-fetch@^3.0.0, whatwg-fetch@^3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"