diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/MapBox/MapBox2.tsx | 641 |
2 files changed, 643 insertions, 1 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f1f5f7e10..b61a468bd 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1074,6 +1074,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' ); + const sidebarWidthPercent = +StrCast(this.layoutDoc.sidebarWidthPercent).replace('%', ''); const titleView = !showTitle ? null : ( <div className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`} @@ -1081,7 +1082,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps style={{ position: this.headerMargin ? 'relative' : 'absolute', height: this.titleHeight, - width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button + width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button color: lightOrDark(background), background, pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx new file mode 100644 index 000000000..37f9bad9e --- /dev/null +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -0,0 +1,641 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api'; +import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc'; +import { Id } from '../../../../fields/FieldSymbols'; +import { InkTool } from '../../../../fields/InkField'; +import { NumCast, StrCast } from '../../../../fields/Types'; +import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { Docs } from '../../../documents/Documents'; +import { DragManager } from '../../../util/DragManager'; +import { SnappingManager } from '../../../util/SnappingManager'; +import { UndoManager } from '../../../util/UndoManager'; +import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; +import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent'; +import { Colors } from '../../global/globalEnums'; +import { MarqueeAnnotator } from '../../MarqueeAnnotator'; +import { AnchorMenu } from '../../pdf/AnchorMenu'; +import { Annotation } from '../../pdf/Annotation'; +import { SidebarAnnos } from '../../SidebarAnnos'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { PinProps } from '../trails'; +import './MapBox2.scss'; +import { MapBoxInfoWindow } from './MapBoxInfoWindow'; + +/** + * MapBox2 architecture: + * Main component: MapBox2.tsx + * Supporting Components: SidebarAnnos, CollectionStackingView + * + * MapBox2 is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content. + * The main body of MapBox2 uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view. + * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available, + * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map). + * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts). + * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps + */ + +// const _global = (window /* browser */ || global /* node */) as any; + +const mapContainerStyle = { + height: '100%', +}; + +const defaultCenter = { + lat: 42.360081, + lng: -71.058884, +}; + +const mapOptions = { + fullscreenControl: false, +}; + +const apiKey = process.env.GOOGLE_MAPS; + +const script = document.createElement('script'); +script.defer = true; +script.async = true; +script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places,drawing`; +console.log(script.src); +document.head.appendChild(script); + +/** + * Consider integrating later: allows for drawing, circling, making shapes on map + */ +// const drawingManager = new window.google.maps.drawing.DrawingManager({ +// drawingControl: true, +// drawingControlOptions: { +// position: google.maps.ControlPosition.TOP_RIGHT, +// drawingModes: [ +// google.maps.drawing.OverlayType.MARKER, +// // currently we are not supporting the following drawing mode on map, a thought for future development +// google.maps.drawing.OverlayType.CIRCLE, +// google.maps.drawing.OverlayType.POLYLINE, +// ], +// }, +// }); + +// options for searchbox in Google Maps Places Autocomplete API +const options = { + fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields + strictBounds: false, + types: ['establishment'], // type pf places, subject of change according to user need +} as google.maps.places.AutocompleteOptions; + +@observer +export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() { + private _dropDisposer?: DragManager.DragDropDisposer; + private _disposers: { [name: string]: IReactionDisposer } = {}; + private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); + @observable private _overlayAnnoInfo: Opt<Doc>; + showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno)); + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(MapBox2, fieldKey); + } + public get SidebarKey() { + return this.fieldKey + '-sidebar'; + } + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); + @computed get inlineTextAnnotations() { + return this.allMapMarkers.filter(a => a.textInlineAnnotations); + } + + @observable private _map: google.maps.Map = null as unknown as google.maps.Map; + @observable private selectedPlace: Doc | undefined; + @observable private markerMap: { [id: string]: google.maps.Marker } = {}; + @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter; + @observable private _marqueeing: number[] | undefined; + @observable private _isAnnotating = false; + @observable private inputRef = React.createRef<HTMLInputElement>(); + @observable private searchMarkers: google.maps.Marker[] = []; + @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options); + @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); + @computed get allSidebarDocs() { + return DocListCast(this.dataDoc[this.SidebarKey]); + } + @computed get allMapMarkers() { + return DocListCast(this.dataDoc[this.annotationKey]); + } + @observable private toggleAddMarker = false; + private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); + + @observable _showSidebar = false; + @computed get SidebarShown() { + return this._showSidebar || this.layoutDoc._showSidebar ? true : false; + } + + static _canAnnotate = true; + static _hadSelection: boolean = false; + private _sidebarRef = React.createRef<SidebarAnnos>(); + private _ref: React.RefObject<HTMLDivElement> = React.createRef(); + + componentDidMount() { + this.props.setContentView?.(this); + } + + @action + private setSearchBox = (searchBox: any) => { + this.searchBox = searchBox; + }; + + // iterate allMarkers to size, center, and zoom map to contain all markers + private fitBounds = (map: google.maps.Map) => { + const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds(); + const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean); + !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds())); + }; + + /** + * Custom control for add marker button + * @param controlDiv + * @param map + */ + private CenterControl = () => { + const controlDiv = document.createElement('div'); + controlDiv.className = 'MapBox2-addMarker'; + // Set CSS for the control border. + const controlUI = document.createElement('div'); + controlUI.style.backgroundColor = '#fff'; + controlUI.style.borderRadius = '3px'; + controlUI.style.cursor = 'pointer'; + controlUI.style.marginTop = '10px'; + controlUI.style.borderRadius = '4px'; + controlUI.style.marginBottom = '22px'; + controlUI.style.textAlign = 'center'; + controlUI.style.position = 'absolute'; + controlUI.style.width = '32px'; + controlUI.style.height = '32px'; + controlUI.title = 'Click to toggle marker mode. In marker mode, click on map to place a marker.'; + + const plIcon = document.createElement('img'); + plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png'; + plIcon.style.color = 'rgb(25,25,25)'; + plIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; + plIcon.style.fontSize = '16px'; + plIcon.style.lineHeight = '32px'; + plIcon.style.left = '18'; + plIcon.style.top = '15'; + plIcon.style.position = 'absolute'; + plIcon.width = 14; + plIcon.height = 14; + plIcon.innerHTML = 'Add'; + controlUI.appendChild(plIcon); + + // Set CSS for the control interior. + const markerIcon = document.createElement('img'); + markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png'; + markerIcon.style.color = 'rgb(25,25,25)'; + markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif'; + markerIcon.style.fontSize = '16px'; + markerIcon.style.lineHeight = '32px'; + markerIcon.style.left = '-2'; + markerIcon.style.top = '1'; + markerIcon.width = 30; + markerIcon.height = 30; + markerIcon.style.position = 'absolute'; + markerIcon.innerHTML = 'Add'; + controlUI.appendChild(markerIcon); + + // Setup the click event listeners + controlUI.addEventListener('click', () => { + if (this.toggleAddMarker === true) { + this.toggleAddMarker = false; + console.log('add marker button status:' + this.toggleAddMarker); + controlUI.style.backgroundColor = '#fff'; + markerIcon.style.color = 'rgb(25,25,25)'; + } else { + this.toggleAddMarker = true; + console.log('add marker button status:' + this.toggleAddMarker); + controlUI.style.backgroundColor = '#4476f7'; + markerIcon.style.color = 'rgb(255,255,255)'; + } + }); + controlDiv.appendChild(controlUI); + return controlDiv; + }; + + /** + * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list + * @param position - the LatLng position where the marker is placed + * @param map + */ + @action + private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => { + const marker = new google.maps.Marker({ + position: position, + map: map, + }); + map.panTo(position); + const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {}); + this.addDocument(mapMarker, this.annotationKey); + }; + + _loadPending = true; + /** + * store a reference to google map instance + * setup the drawing manager on the top right corner of map + * fit map bounds to contain all markers + * @param map + */ + @action + private loadHandler = (map: google.maps.Map) => { + this._map = map; + this._loadPending = true; + const centerControlDiv = this.CenterControl(); + map.controls[google.maps.ControlPosition.TOP_RIGHT].push(centerControlDiv); + //drawingManager.setMap(map); + // if (navigator.geolocation) { + // navigator.geolocation.getCurrentPosition( + // (position: Position) => { + // const pos = { + // lat: position.coords.latitude, + // lng: position.coords.longitude, + // }; + // this._map.setCenter(pos); + // } + // ); + // } else { + // alert("Your geolocation is not supported by browser.") + // }; + map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5)); + map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng))); + setTimeout(() => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + }, 250); + // listener to addmarker event + this._map.addListener('click', (e: MouseEvent) => { + if (this.toggleAddMarker === true) { + this.placeMarker((e as any).latLng, map); + } + }); + }; + + @action + centered = () => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + this.dataDoc.mapLat = this._map.getCenter()?.lat(); + this.dataDoc.mapLng = this._map.getCenter()?.lng(); + }; + + @action + zoomChanged = () => { + if (this._loadPending && this._map.getBounds()) { + this._loadPending = false; + this.layoutDoc.fitContentsToBox && this.fitBounds(this._map); + } + this.dataDoc.mapZoom = this._map.getZoom(); + }; + + /** + * Load and render all map markers + * @param marker + * @param place + */ + @action + private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => { + place[Id] ? (this.markerMap[place[Id]] = marker) : null; + }; + + /** + * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true + * @param e + * @param place + */ + @action + private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => { + // set which place was clicked + this.selectedPlace = place; + place.infoWindowOpen = true; + }; + + /** + * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts + * @param doc + * @param sidebarKey + * @returns + */ + sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => { + console.log('print all sidebar Docs'); + if (!this.layoutDoc._showSidebar) this.toggleSidebar(); + const docs = doc instanceof Doc ? [doc] : doc; + docs.forEach(doc => { + if (doc.lat !== undefined && doc.lng !== undefined) { + const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng); + if (existingMarker) { + Doc.AddDocToList(existingMarker, 'data', doc); + } else { + const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {}); + this.addDocument(marker, this.annotationKey); + } + } + }); //add to annotation list + + return this.addDocument(doc, sidebarKey); // add to sidebar list + }; + + /** + * Removing documents from the sidebar + * @param doc + * @param sidebarKey + * @returns + */ + sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => { + if (this.layoutDoc._showSidebar) this.toggleSidebar(); + const docs = doc instanceof Doc ? [doc] : doc; + return this.removeDocument(doc, sidebarKey); + }; + + /** + * Toggle sidebar onclick the tiny comment button on the top right corner + * @param e + */ + sidebarBtnDown = (e: React.PointerEvent) => { + setupMoveUpEvents( + this, + e, + (e, down, delta) => + runInAction(() => { + const localDelta = this.props + .ScreenToLocalTransform() + .scale(this.props.NativeDimScaling?.() || 1) + .transformDirection(delta[0], delta[1]); + const fullWidth = this.layoutDoc[WidthSym](); + const mapWidth = fullWidth - this.sidebarWidth(); + if (this.sidebarWidth() + localDelta[0] > 0) { + this._showSidebar = true; + this.layoutDoc._width = fullWidth + localDelta[0]; + this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%'; + } else { + this._showSidebar = false; + this.layoutDoc._width = mapWidth; + this.layoutDoc._sidebarWidthPercent = '0%'; + } + return false; + }), + emptyFunction, + () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map') + ); + }; + + sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + @computed get sidebarWidthPercent() { + return StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); + } + @computed get sidebarColor() { + return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4')); + } + + /** + * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted; + * add a customized temporary marker on the map + */ + @action + private handlePlaceChanged = () => { + const place = this.searchBox.getPlace(); + + if (!place.geometry || !place.geometry.location) { + // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed + window.alert("No details available for input: '" + place.name + "'"); + return; + } + + // zoom in on the location of the search result + if (place.geometry.viewport) { + this._map.fitBounds(place.geometry.viewport); + } else { + this._map.setCenter(place.geometry.location); + this._map.setZoom(17); + } + + // customize icon => customized icon for the nature of the location selected + const icon = { + url: place.icon as string, + size: new google.maps.Size(71, 71), + origin: new google.maps.Point(0, 0), + anchor: new google.maps.Point(17, 34), + scaledSize: new google.maps.Size(25, 25), + }; + + // put temporary cutomized marker on searched location + this.searchMarkers.forEach(marker => { + marker.setMap(null); + }); + this.searchMarkers = []; + this.searchMarkers.push( + new window.google.maps.Marker({ + map: this._map, + icon, + title: place.name, + position: place.geometry.location, + }) + ); + }; + + /** + * Handles toggle of sidebar on click the little comment button + */ + @computed get sidebarHandle() { + return ( + <div + className="MapBox2-overlayButton-sidebar" + key="sidebar" + title="Toggle Sidebar" + style={{ + display: !this.props.isContentActive() ? 'none' : undefined, + top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5, + backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK, + }} + onPointerDown={this.sidebarBtnDown}> + <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" /> + </div> + ); + } + + // TODO: Adding highlight box layer to Maps + @action + toggleSidebar = () => { + //1.2 * w * ? = .2 * w .2/1.2 + const prevWidth = this.sidebarWidth(); + this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%'; + this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth); + }; + + sidebarDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true); + }; + sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => { + const bounds = this._ref.current!.getBoundingClientRect(); + this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%'; + this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%'; + e.preventDefault(); + return false; + }; + + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); + + @action + onMarqueeDown = (e: React.PointerEvent) => { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + setupMoveUpEvents( + this, + e, + action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), + returnFalse, + () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), + false + ); + } + }; + @action finishMarquee = (x?: number, y?: number) => { + this._marqueeing = undefined; + this._isAnnotating = false; + x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); + }; + + addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => { + return this.addDocument(doc, annotationKey); + }; + + pointerEvents = () => { + return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'; + }; + @computed get annotationLayer() { + return ( + <div className="MapBox2-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}> + {this.inlineTextAnnotations + .sort((a, b) => NumCast(a.y) - NumCast(b.y)) + .map(anno => ( + <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} /> + ))} + </div> + ); + } + + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; + + /** + * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker + * @returns + */ + private renderMarkers = () => { + return this.allMapMarkers.map(place => ( + <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} /> + )); + }; + + // TODO: auto center on select a document in the sidebar + private handleMapCenter = (map: google.maps.Map) => { + // console.log("print the selected views in selectionManager:") + // if (SelectionManager.Views().lastElement()) { + // console.log(SelectionManager.Views().lastElement()); + // } + }; + + panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth(); + panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); + scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); + transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + infoWidth = () => this.props.PanelWidth() / 5; + infoHeight = () => this.props.PanelHeight() / 5; + anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; + savedAnnotations = () => this._savedAnnotations; + + get MicrosoftMaps() { + return (window as any).Microsoft.Maps; + } + render() { + const renderAnnotations = (docFilters?: () => string[]) => null; + return ( + <div className="MapBox2" ref={this._ref}> + <div + className="MapBox2-wrapper" + onWheel={e => e.stopPropagation()} + onPointerDown={async e => { + e.button === 0 && !e.ctrlKey && e.stopPropagation(); + }} + style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}> + <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div> + {renderAnnotations(this.opaqueFilter)} + {SnappingManager.GetIsDragging() ? null : renderAnnotations()} + {this.annotationLayer} + + <div> + <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}> + <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}> + <input className="MapBox2-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" /> + </Autocomplete> + + {this.renderMarkers()} + {this.allMapMarkers + .filter(marker => marker.infoWindowOpen) + .map(marker => ( + <MapBoxInfoWindow + key={marker[Id]} + {...this.props} + setContentView={emptyFunction} + place={marker} + markerMap={this.markerMap} + PanelWidth={this.infoWidth} + PanelHeight={this.infoHeight} + moveDocument={this.moveDocument} + isAnyChildContentActive={this.isAnyChildContentActive} + whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} + /> + ))} + {/* {this.handleMapCenter(this._map)} */} + </GoogleMap> + </div> + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( + <MarqueeAnnotator + rootDoc={this.rootDoc} + anchorMenuClick={this.anchorMenuClick} + scrollTop={0} + down={this._marqueeing} + scaling={returnOne} + addDocument={this.addDocumentWrapper} + docView={this.props.docViewPath().lastElement()} + finishMarquee={this.finishMarquee} + savedAnnotations={this.savedAnnotations} + annotationLayer={this._annotationLayer.current} + selectionText={returnEmptyString} + mainCont={this._mainCont.current} + /> + )} + </div> + {/* </LoadScript > */} + <div className="MapBox2-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> + <SidebarAnnos + ref={this._sidebarRef} + {...this.props} + fieldKey={this.fieldKey} + rootDoc={this.rootDoc} + layoutDoc={this.layoutDoc} + dataDoc={this.dataDoc} + usePanelWidth={true} + showSidebar={this.SidebarShown} + nativeWidth={NumCast(this.layoutDoc._nativeWidth)} + whenChildContentsActiveChanged={this.whenChildContentsActiveChanged} + PanelWidth={this.sidebarWidth} + sidebarAddDocument={this.sidebarAddDocument} + moveDocument={this.moveDocument} + removeDocument={this.sidebarRemoveDocument} + /> + </div> + {this.sidebarHandle} + </div> + ); + } +} |