ToolJet/frontend/src/Editor/Components/Map/Map.jsx

163 lines
4.9 KiB
React
Raw Normal View History

import React, { useState, useCallback } from 'react';
import { GoogleMap, LoadScript, Marker, Autocomplete } from '@react-google-maps/api';
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
import { darkModeStyles } from './styles';
2021-05-16 05:26:31 +00:00
export const Map = function Map({
id,
width,
height,
component,
darkMode,
2021-05-16 05:26:31 +00:00
onComponentClick,
currentState,
onComponentOptionChanged,
onComponentOptionsChanged,
onEvent,
2021-12-30 11:57:02 +00:00
// canvasWidth,
2021-05-16 05:26:31 +00:00
}) {
2021-05-16 06:03:26 +00:00
const center = component.definition.properties.initialLocation.value;
2021-05-16 06:25:48 +00:00
const defaultMarkerValue = component.definition.properties.defaultMarkers.value;
let defaultMarkers = [];
try {
defaultMarkers = defaultMarkerValue;
} catch (err) {
console.log(err);
}
2021-05-16 08:56:18 +00:00
const addNewMarkersProp = component.definition.properties.addNewMarkers;
const canAddNewMarkers = addNewMarkersProp ? addNewMarkersProp.value : false;
2021-05-16 05:26:31 +00:00
2021-05-17 15:49:18 +00:00
const canSearchProp = component.definition.properties.canSearch;
const canSearch = canSearchProp ? canSearchProp.value : false;
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
const disabledState = component.definition.styles?.disabledState?.value ?? false;
const parsedDisabledState =
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
2021-08-30 11:43:05 +00:00
let parsedWidgetVisibility = widgetVisibility;
2021-08-31 16:36:31 +00:00
2021-08-30 11:43:05 +00:00
try {
parsedWidgetVisibility = resolveReferences(parsedWidgetVisibility, currentState, []);
} catch (err) {
console.log(err);
}
2021-05-17 15:49:18 +00:00
const [gmap, setGmap] = useState(null);
const [autoComplete, setAutoComplete] = useState(null);
2021-08-31 16:36:31 +00:00
const [mapCenter, setMapCenter] = useState(resolveReferences(center, currentState));
const [markers, setMarkers] = useState(resolveReferences(defaultMarkers, currentState));
2021-05-16 05:26:31 +00:00
const containerStyle = {
width: '100%',
height,
2021-05-16 05:26:31 +00:00
};
function handleMapClick(e) {
if (!canAddNewMarkers) {
return;
}
2021-05-16 08:56:18 +00:00
const lat = e.latLng.lat();
const lng = e.latLng.lng();
const newMarkers = markers;
newMarkers.push({ lat, lng });
setMarkers(newMarkers);
2021-05-16 10:11:55 +00:00
onComponentOptionChanged(component, 'markers', newMarkers).then(() => onEvent('onCreateMarker', { component }));
}
2021-05-16 05:26:31 +00:00
function handleBoundsChange() {
const mapBounds = gmap.getBounds();
const bounds = {
2021-09-01 04:13:00 +00:00
northEast: mapBounds.getNorthEast()?.toJSON(),
southWest: mapBounds.getSouthWest()?.toJSON(),
};
2021-09-01 04:13:00 +00:00
const newCenter = gmap.center?.toJSON();
setMapCenter(newCenter);
onComponentOptionsChanged(component, [
['bounds', bounds],
['center', newCenter],
2021-05-16 09:09:24 +00:00
]).then(() => onEvent('onBoundsChange', { component }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
const onLoad = useCallback(function onLoad(mapInstance) {
setGmap(mapInstance);
onComponentOptionsChanged(component, [['center', mapInstance.center?.toJSON()]]);
});
2021-05-16 10:11:55 +00:00
function handleMarkerClick(index) {
onComponentOptionChanged(component, 'selectedMarker', markers[index]).then(() =>
onEvent('onMarkerClick', { component })
);
2021-05-16 10:11:55 +00:00
}
function onPlaceChanged() {
2021-09-01 04:13:00 +00:00
const location = autoComplete.getPlace().geometry.location?.toJSON();
setMapCenter(location);
handleBoundsChange();
}
function onAutocompleteLoad(autocompleteInstance) {
setAutoComplete(autocompleteInstance);
}
2021-05-16 05:26:31 +00:00
return (
<div
data-disabled={parsedDisabledState}
style={{ height, display: parsedWidgetVisibility ? '' : 'none' }}
onClick={(event) => {
event.stopPropagation();
Feature: Collaboration ( realtime comments for canvas ) 🔥 (#810) * feat: initial commit for collaboration feature * add dnd to comments * add positions endpoint * feat: encapsulate all http common logic in http-client * segregate sections and transfer responsibility of state * feat: use-spring to add fade effect :zap: * fix: open in right * fix: left-right position css * add footer for message * integrate getcomment endpoint * use fromnow for date ago * add dnd * - Add data trasfer object for comment - Add class-validator package to check the response type from client - Add comment repository class for persistance layer - Add comment service with std. http methods - Update controller with all http methods - Update comment module - Fix http-client bug when error is thrown * fix http client bug when error is thrown * feat: add entity thread * feat: add migrations for thread and comment * update entitites * add tid to migration * filter comments by tid(thread_id) * fix: comment migration, add missing column comment * feat: integrate in ui * feat: split comments based on app_id * fix: dnd to correct position * package json engines * engines update * update npm * npm 6 to 7 * fix: add user initials to thread * fix: add firtname lastname to the comments * - Return user object when save thread called - Hide password field from user response - Fix created_at date typo - Instead of fetch all threads on new thread added, add the response to array of existing threads * feat: update ui components * change icon on comments view * ui fixes * fix: close icon close the popover * temp: comment select: false * use currentUser from localStorage * fix: on click outside if comment is open, dont hit addThread * fix: auth token issue in http-client * on drag hide the comment if open * add jwt auth * spec: add test for comment & thread * cleanup: remove console.log * feat: add comment actions * feat: add edit, delete, resolve options * feat: add mentions component * feat: add nestjs websockets * temp * websocket: establish client-server communication * ws: add message listner to comments module in ui * feat: add broadcast method to broadcast new events to all clients :bomb: * ws: cleanup :call_me_hand: * fix: remove max height from comment actions * feat: add user mentions, emoji support * fix: add static list of users - temp * update and delete iterations * - Rename comment, thread to comments, threads - Add conditional actions - Show edit, delete only if he is comment owner - Show resolve only if he is thread owner * reset engines * move svgr webpack to deps * fix: ui issues * remove log stmt * refactor: move resolved icon to comment-header * feat: allow comments to be added on top of widgets * feat: add keyboard shortcut * scroll to bottom on comment add * ui fixes * feat: add react toast for notification display * feat: add comment badge * fix: ws connection * fix: ws * remove rvrse * feat: add comment sidebar * feat: add comment right sidebar * fix: add missing foreign key elements * - upgrade typeorm to 0.2.38 - comment sidebar ui - added filter ui * feat: on click of right sidebar notificaiton open the comment box * reset engines * fix: add organization id to the comment and thread module * fix: add current version id * add currentversion id * disable comments if no id present * temp:checking for heroku deploy * fetch app on edit and deploy version * rename current_version_id to app_versions_id * ui fixes * show mentioned user in blue color * add ui changes * add authorization for create thread * change color to blue on click of comment, add auth for other endpoints of thread * update threads, notifications using socket * add auth for comments * remove events spec file * fix duplicate key error * fix notificaitons updation on edit, delete, resolve buttons clicked * update notifications for edit * feature toggle changes for frontend * add check for comments server * add emoji mart package for emoji * add reply count in comment sidebar * subtract 1 from count in comment sidebar * change empty text when no comments available
2021-11-01 07:28:03 +00:00
onComponentClick(id, component, event);
}}
className="map-widget"
>
<div
className="map-center"
style={{
right: width * 0.5 - 18,
top: height * 0.5 - 50,
}}
2021-05-16 05:26:31 +00:00
>
<img className="mx-2" src="/assets/images/icons/marker.svg" width="24" height="64" />
</div>
<LoadScript googleMapsApiKey={window.public_config.GOOGLE_MAPS_API_KEY} libraries={['places']}>
2021-05-16 05:26:31 +00:00
<GoogleMap
center={mapCenter}
2021-05-16 05:26:31 +00:00
mapContainerStyle={containerStyle}
zoom={12}
options={{
styles: darkMode === true ? darkModeStyles : '',
2021-05-16 05:26:31 +00:00
streetViewControl: false,
mapTypeControl: false,
draggable: true,
2021-05-16 05:26:31 +00:00
}}
onLoad={onLoad}
onClick={handleMapClick}
onDragEnd={handleBoundsChange}
2021-05-16 05:26:31 +00:00
>
{canSearch && (
<Autocomplete onPlaceChanged={onPlaceChanged} onLoad={onAutocompleteLoad}>
<input type="text" placeholder="Search" className="place-search-input" />
</Autocomplete>
)}
{Array.isArray(markers) && (
2021-05-16 18:13:57 +00:00
<>
{markers.map((marker, index) => (
<Marker key={index} position={marker} label={marker.label} onClick={() => handleMarkerClick(index)} />
))}
2021-05-16 18:13:57 +00:00
</>
)}
2021-05-16 05:26:31 +00:00
</GoogleMap>
</LoadScript>
</div>
);
2021-08-31 16:36:31 +00:00
};