diff --git a/android/src/main/java/com/luggmaps/LuggMarkerView.kt b/android/src/main/java/com/luggmaps/LuggMarkerView.kt index 087f432..dc71480 100644 --- a/android/src/main/java/com/luggmaps/LuggMarkerView.kt +++ b/android/src/main/java/com/luggmaps/LuggMarkerView.kt @@ -11,6 +11,7 @@ import com.facebook.react.views.view.ReactViewGroup import com.google.android.gms.maps.model.AdvancedMarker import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.luggmaps.events.MarkerDragEvent import com.luggmaps.events.MarkerPressEvent import com.luggmaps.extensions.dispatchEvent @@ -58,6 +59,11 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) { var rasterize: Boolean = true private set + var draggable: Boolean = false + private set + + var isDragging: Boolean = false + var didLayout: Boolean = false private set @@ -240,10 +246,26 @@ class LuggMarkerView(context: Context) : ReactViewGroup(context) { this.rasterize = rasterize } + fun setDraggable(draggable: Boolean) { + this.draggable = draggable + } + fun emitPressEvent(x: Float, y: Float) { dispatchEvent(MarkerPressEvent(this, latitude, longitude, x, y)) } + fun emitDragStartEvent(x: Float, y: Float) { + dispatchEvent(MarkerDragEvent(this, MarkerDragEvent.DRAG_START, latitude, longitude, x, y)) + } + + fun emitDragChangeEvent(x: Float, y: Float) { + dispatchEvent(MarkerDragEvent(this, MarkerDragEvent.DRAG_CHANGE, latitude, longitude, x, y)) + } + + fun emitDragEndEvent(x: Float, y: Float) { + dispatchEvent(MarkerDragEvent(this, MarkerDragEvent.DRAG_END, latitude, longitude, x, y)) + } + fun setName(name: String?) { this.name = name } diff --git a/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt b/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt index b6fffad..4defe0c 100644 --- a/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +++ b/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt @@ -21,7 +21,12 @@ class LuggMarkerViewManager : override fun createViewInstance(context: ThemedReactContext): LuggMarkerView = LuggMarkerView(context) override fun getExportedCustomDirectEventTypeConstants(): Map = - mapOf("topMarkerPress" to mapOf("registrationName" to "onMarkerPress")) + mapOf( + "topMarkerPress" to mapOf("registrationName" to "onMarkerPress"), + "topMarkerDragStart" to mapOf("registrationName" to "onMarkerDragStart"), + "topMarkerDragChange" to mapOf("registrationName" to "onMarkerDragChange"), + "topMarkerDragEnd" to mapOf("registrationName" to "onMarkerDragEnd"), + ) override fun onDropViewInstance(view: LuggMarkerView) { super.onDropViewInstance(view) @@ -87,6 +92,11 @@ class LuggMarkerViewManager : view.setRasterize(value) } + @ReactProp(name = "draggable", defaultBoolean = false) + override fun setDraggable(view: LuggMarkerView, value: Boolean) { + view.setDraggable(value) + } + companion object { const val NAME = "LuggMarkerView" } diff --git a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt index 4a559c3..9324fef 100644 --- a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +++ b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt @@ -37,7 +37,8 @@ class GoogleMapProvider(private val context: Context) : GoogleMap.OnMapClickListener, GoogleMap.OnMapLongClickListener, GoogleMap.OnPolygonClickListener, - GoogleMap.OnMarkerClickListener { + GoogleMap.OnMarkerClickListener, + GoogleMap.OnMarkerDragListener { override var delegate: MapProviderDelegate? = null override val isMapReady: Boolean get() = _isMapReady @@ -118,6 +119,7 @@ class GoogleMapProvider(private val context: Context) : googleMap?.setOnMapLongClickListener(null) googleMap?.setOnPolygonClickListener(null) googleMap?.setOnMarkerClickListener(null) + googleMap?.setOnMarkerDragListener(null) googleMap?.clear() googleMap = null _isMapReady = false @@ -140,6 +142,7 @@ class GoogleMapProvider(private val context: Context) : map.setOnMapLongClickListener(this) map.setOnPolygonClickListener(this) map.setOnMarkerClickListener(this) + map.setOnMarkerDragListener(this) wrapperView?.touchEventHandler = { event -> if (event.action == android.view.MotionEvent.ACTION_DOWN) { @@ -215,6 +218,32 @@ class GoogleMapProvider(private val context: Context) : return false } + override fun onMarkerDragStart(marker: Marker) { + markerToViewMap[marker]?.let { view -> + view.isDragging = true + view.setCoordinate(marker.position.latitude, marker.position.longitude) + val point = googleMap?.projection?.toScreenLocation(marker.position) + view.emitDragStartEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f) + } + } + + override fun onMarkerDrag(marker: Marker) { + markerToViewMap[marker]?.let { view -> + view.setCoordinate(marker.position.latitude, marker.position.longitude) + val point = googleMap?.projection?.toScreenLocation(marker.position) + view.emitDragChangeEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f) + } + } + + override fun onMarkerDragEnd(marker: Marker) { + markerToViewMap[marker]?.let { view -> + view.isDragging = false + view.setCoordinate(marker.position.latitude, marker.position.longitude) + val point = googleMap?.projection?.toScreenLocation(marker.position) + view.emitDragEndEvent(point?.x?.toFloat() ?: 0f, point?.y?.toFloat() ?: 0f) + } + } + // endregion // region Props @@ -371,12 +400,15 @@ class GoogleMapProvider(private val context: Context) : } markerView.marker?.apply { - position = LatLng(markerView.latitude, markerView.longitude) + if (!markerView.isDragging) { + position = LatLng(markerView.latitude, markerView.longitude) + } title = markerView.title snippet = markerView.description setAnchor(markerView.anchorX, markerView.anchorY) zIndex = markerView.zIndex rotation = markerView.rotate + isDraggable = markerView.draggable } if (markerView.hasCustomView && markerView.scaleChanged) { @@ -404,6 +436,7 @@ class GoogleMapProvider(private val context: Context) : marker.setAnchor(markerView.anchorX, markerView.anchorY) marker.zIndex = markerView.zIndex marker.rotation = markerView.rotate + marker.isDraggable = markerView.draggable markerView.marker = marker markerToViewMap[marker] = markerView diff --git a/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt b/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt new file mode 100644 index 0000000..aab6a76 --- /dev/null +++ b/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt @@ -0,0 +1,41 @@ +package com.luggmaps.events + +import android.view.View +import com.facebook.react.bridge.Arguments +import com.facebook.react.uimanager.UIManagerHelper +import com.facebook.react.uimanager.events.Event + +class MarkerDragEvent( + view: View, + private val eventType: String, + private val latitude: Double, + private val longitude: Double, + private val x: Float, + private val y: Float +) : Event(UIManagerHelper.getSurfaceId(view), view.id) { + override fun getEventName() = eventType + + override fun getEventData() = + Arguments.createMap().apply { + putMap( + "coordinate", + Arguments.createMap().apply { + putDouble("latitude", latitude) + putDouble("longitude", longitude) + } + ) + putMap( + "point", + Arguments.createMap().apply { + putDouble("x", x.toDouble()) + putDouble("y", y.toDouble()) + } + ) + } + + companion object { + const val DRAG_START = "topMarkerDragStart" + const val DRAG_CHANGE = "topMarkerDragChange" + const val DRAG_END = "topMarkerDragEnd" + } +} diff --git a/docs/MARKER.md b/docs/MARKER.md index fb67c38..ce3b5ac 100644 --- a/docs/MARKER.md +++ b/docs/MARKER.md @@ -39,9 +39,27 @@ import { MapView, Marker } from '@lugg/maps'; | `rotate` | `number` | `0` | Rotation angle in degrees clockwise from north | | `scale` | `number` | `1` | Scale factor for the marker | | `rasterize` | `boolean` | `true` | Rasterize custom marker view to bitmap (iOS/Android only) | +| `draggable` | `boolean` | `false` | Whether the marker can be dragged by the user | | `onPress` | `(event: MarkerPressEvent) => void` | - | Called when the marker is pressed. Event includes `coordinate` and `point` | +| `onDragStart` | `(event: MarkerDragEvent) => void` | - | Called when marker drag starts. Event includes `coordinate` and `point` | +| `onDragChange` | `(event: MarkerDragEvent) => void` | - | Called continuously as the marker is dragged. Event includes `coordinate` and `point` | +| `onDragEnd` | `(event: MarkerDragEvent) => void` | - | Called when marker drag ends. Event includes `coordinate` and `point` | | `children` | `ReactNode` | - | Custom marker view | +## Draggable Markers + +Set `draggable` to enable marker dragging. Use the drag event callbacks to track position changes. + +```tsx + console.log('Drag started', e.nativeEvent.coordinate)} + onDragChange={(e) => console.log('Dragging', e.nativeEvent.coordinate)} + onDragEnd={(e) => setMarkerCoordinate(e.nativeEvent.coordinate)} +/> +``` + ## Custom Markers Use the `children` prop to render a custom marker view. The `anchor` prop controls the point that is placed at the coordinate. diff --git a/example/shared/src/Home.tsx b/example/shared/src/Home.tsx index 8b1d575..a5b1ad3 100644 --- a/example/shared/src/Home.tsx +++ b/example/shared/src/Home.tsx @@ -194,6 +194,13 @@ function HomeContent() { onCameraMove={(e) => formatCameraEvent(e, false)} onCameraIdle={(e) => formatCameraEvent(e, true)} onMarkerPress={(e, m) => formatPressEvent(e, `Marker(${m.name})`)} + onMarkerDragStart={(e, m) => + formatPressEvent(e, `Drag start(${m.name})`) + } + onMarkerDragChange={(e, m) => + formatPressEvent(e, `Dragging(${m.name})`) + } + onMarkerDragEnd={(e, m) => formatPressEvent(e, `Drag end(${m.name})`)} onPolygonPress={() => { lockStatus(); setStatusText('Polygon pressed'); diff --git a/example/shared/src/components/Map.tsx b/example/shared/src/components/Map.tsx index e493021..3e76b5f 100644 --- a/example/shared/src/components/Map.tsx +++ b/example/shared/src/components/Map.tsx @@ -7,6 +7,7 @@ import { type MapViewProps, type MapCameraEvent, type MarkerPressEvent, + type MarkerDragEvent, } from '@lugg/maps'; import Animated, { useAnimatedStyle, @@ -25,6 +26,9 @@ interface MapProps extends MapViewProps { animatedPosition?: SharedValue; onPolygonPress?: () => void; onMarkerPress?: (event: MarkerPressEvent, marker: MarkerData) => void; + onMarkerDragStart?: (event: MarkerDragEvent, marker: MarkerData) => void; + onMarkerDragChange?: (event: MarkerDragEvent, marker: MarkerData) => void; + onMarkerDragEnd?: (event: MarkerDragEvent, marker: MarkerData) => void; } const INITIAL_ZOOM = 14; @@ -44,7 +48,10 @@ const CIRCLE_COORDS = Array.from({ length: 36 }, (_, i) => { const renderMarker = ( marker: MarkerData, - onPress?: (event: MarkerPressEvent, marker: MarkerData) => void + onPress?: (event: MarkerPressEvent, marker: MarkerData) => void, + onDragStart?: (event: MarkerDragEvent, marker: MarkerData) => void, + onDragChange?: (event: MarkerDragEvent, marker: MarkerData) => void, + onDragEnd?: (event: MarkerDragEvent, marker: MarkerData) => void ) => { const { id, @@ -62,6 +69,15 @@ const renderMarker = ( const handlePress = onPress ? (e: MarkerPressEvent) => onPress(e, marker) : undefined; + const handleDragStart = onDragStart + ? (e: MarkerDragEvent) => onDragStart(e, marker) + : undefined; + const handleDragChange = onDragChange + ? (e: MarkerDragEvent) => onDragChange(e, marker) + : undefined; + const handleDragEnd = onDragEnd + ? (e: MarkerDragEvent) => onDragEnd(e, marker) + : undefined; switch (type) { case 'icon': @@ -70,7 +86,11 @@ const renderMarker = ( key={id} name={name} coordinate={coordinate} + draggable onPress={handlePress} + onDragStart={handleDragStart} + onDragChange={handleDragChange} + onDragEnd={handleDragEnd} /> ); case 'text': @@ -81,7 +101,11 @@ const renderMarker = ( coordinate={coordinate} text={text ?? 'X'} color={color} + draggable onPress={handlePress} + onDragStart={handleDragStart} + onDragChange={handleDragChange} + onDragEnd={handleDragEnd} /> ); case 'image': @@ -91,7 +115,11 @@ const renderMarker = ( name={name} coordinate={coordinate} source={{ uri: imageUrl }} + draggable onPress={handlePress} + onDragStart={handleDragStart} + onDragChange={handleDragChange} + onDragEnd={handleDragEnd} /> ); case 'custom': @@ -101,7 +129,11 @@ const renderMarker = ( name={name} coordinate={coordinate} anchor={anchor} + draggable onPress={handlePress} + onDragStart={handleDragStart} + onDragChange={handleDragChange} + onDragEnd={handleDragEnd} > ); } @@ -134,6 +170,9 @@ export const Map = forwardRef( onLongPress, onPolygonPress, onMarkerPress, + onMarkerDragStart, + onMarkerDragChange, + onMarkerDragEnd, ...props }, ref @@ -182,7 +221,15 @@ export const Map = forwardRef( onCameraIdle={handleCameraIdle} {...props} > - {markers.map((m) => renderMarker(m, onMarkerPress))} + {markers.map((m) => + renderMarker( + m, + onMarkerPress, + onMarkerDragStart, + onMarkerDragChange, + onMarkerDragEnd + ) + )} @@ -24,6 +25,7 @@ @implementation LuggMarkerView { CLLocationDegrees _rotate; CGFloat _scale; BOOL _rasterize; + BOOL _draggable; BOOL _didLayout; UIView *_iconView; } @@ -74,6 +76,7 @@ - (void)updateProps:(Props::Shared const &)props _rotate = newViewProps.rotate; _scale = newViewProps.scale; _rasterize = newViewProps.rasterize; + _draggable = newViewProps.draggable; } - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { @@ -161,6 +164,10 @@ - (BOOL)rasterize { return _rasterize; } +- (BOOL)draggable { + return _draggable; +} + - (BOOL)hasCustomView { return _iconView.subviews.count > 0; } @@ -220,6 +227,40 @@ - (void)emitPressEventWithPoint:(CGPoint)point { event.emit(_eventEmitter); } +- (void)emitDragStartEventWithPoint:(CGPoint)point { + MarkerDragStartEvent event{ + .latitude = _coordinate.latitude, + .longitude = _coordinate.longitude, + .x = point.x, + .y = point.y, + }; + event.emit(_eventEmitter); +} + +- (void)emitDragChangeEventWithPoint:(CGPoint)point { + MarkerDragChangeEvent event{ + .latitude = _coordinate.latitude, + .longitude = _coordinate.longitude, + .x = point.x, + .y = point.y, + }; + event.emit(_eventEmitter); +} + +- (void)emitDragEndEventWithPoint:(CGPoint)point { + MarkerDragEndEvent event{ + .latitude = _coordinate.latitude, + .longitude = _coordinate.longitude, + .x = point.x, + .y = point.y, + }; + event.emit(_eventEmitter); +} + +- (void)updateCoordinate:(CLLocationCoordinate2D)coordinate { + _coordinate = coordinate; +} + - (void)resetIconViewTransform { _iconView.transform = CGAffineTransformIdentity; _iconView.layer.anchorPoint = CGPointMake(0.5, 0.5); diff --git a/ios/core/AppleMapProvider.mm b/ios/core/AppleMapProvider.mm index c7f9947..0eb0490 100644 --- a/ios/core/AppleMapProvider.mm +++ b/ios/core/AppleMapProvider.mm @@ -34,7 +34,6 @@ @implementation AppleMapProvider { NSMapTable, LuggPolygonView *> *_overlayToPolygonMap; UITapGestureRecognizer *_tapGesture; UILongPressGestureRecognizer *_longPressGesture; - // Edge insets animation CADisplayLink *_edgeInsetsDisplayLink; UIEdgeInsets _edgeInsetsFrom; @@ -407,10 +406,24 @@ - (MKAnnotationView *)mapView:(MKMapView *)mapView AppleMarkerAnnotation *markerAnnotation = (AppleMarkerAnnotation *)annotation; LuggMarkerView *markerView = markerAnnotation.markerView; - if (!markerView || !markerView.hasCustomView) { + if (!markerView) { return nil; } + if (!markerView.hasCustomView) { + MKMarkerAnnotationView *markerAnnotationView = + [[MKMarkerAnnotationView alloc] initWithAnnotation:annotation + reuseIdentifier:nil]; + markerAnnotationView.canShowCallout = YES; + markerAnnotationView.displayPriority = MKFeatureDisplayPriorityRequired; + markerAnnotationView.layer.zPosition = markerView.zIndex; + markerAnnotationView.zPriority = markerView.zIndex; + markerAnnotationView.draggable = markerView.draggable; + [self addCenterTapGesture:markerAnnotationView]; + markerAnnotation.annotationView = markerAnnotationView; + return markerAnnotationView; + } + MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil]; @@ -418,6 +431,8 @@ - (MKAnnotationView *)mapView:(MKMapView *)mapView annotationView.displayPriority = MKFeatureDisplayPriorityRequired; annotationView.layer.zPosition = markerView.zIndex; annotationView.zPriority = markerView.zIndex; + annotationView.draggable = markerView.draggable; + [self addCenterTapGesture:annotationView]; if (!markerView.rasterize) { UIView *iconView = markerView.iconView; @@ -490,7 +505,62 @@ - (void)mapView:(MKMapView *)mapView CGPoint point = [_mapView convertCoordinate:markerView.coordinate toPointToView:_mapView]; [markerView emitPressEventWithPoint:point]; - [_mapView setCenterCoordinate:markerView.coordinate animated:YES]; + } +} + +- (void)mapView:(MKMapView *)mapView + annotationView:(MKAnnotationView *)view + didChangeDragState:(MKAnnotationViewDragState)newState + fromOldState:(MKAnnotationViewDragState)oldState { + if (![view.annotation isKindOfClass:[AppleMarkerAnnotation class]]) + return; + + AppleMarkerAnnotation *annotation = + (AppleMarkerAnnotation *)view.annotation; + LuggMarkerView *markerView = annotation.markerView; + if (!markerView) + return; + + CLLocationCoordinate2D coord = annotation.coordinate; + CGPoint point = [_mapView convertCoordinate:coord toPointToView:_mapView]; + + switch (newState) { + case MKAnnotationViewDragStateStarting: + [markerView updateCoordinate:coord]; + [markerView emitDragStartEventWithPoint:point]; + [view setDragState:MKAnnotationViewDragStateDragging animated:YES]; + break; + case MKAnnotationViewDragStateDragging: + [markerView updateCoordinate:coord]; + [markerView emitDragChangeEventWithPoint:point]; + break; + case MKAnnotationViewDragStateEnding: + [markerView updateCoordinate:coord]; + [markerView emitDragEndEventWithPoint:point]; + [view setDragState:MKAnnotationViewDragStateNone animated:YES]; + break; + case MKAnnotationViewDragStateCanceling: + [view setDragState:MKAnnotationViewDragStateNone animated:YES]; + break; + default: + break; + } +} + +- (void)addCenterTapGesture:(MKAnnotationView *)view { + UITapGestureRecognizer *tap = + [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(handleAnnotationTap:)]; + tap.cancelsTouchesInView = NO; + [view addGestureRecognizer:tap]; +} + +- (void)handleAnnotationTap:(UITapGestureRecognizer *)gesture { + MKAnnotationView *view = (MKAnnotationView *)gesture.view; + if ([view.annotation isKindOfClass:[AppleMarkerAnnotation class]]) { + AppleMarkerAnnotation *annotation = + (AppleMarkerAnnotation *)view.annotation; + [_mapView setCenterCoordinate:annotation.coordinate animated:YES]; } } @@ -517,6 +587,7 @@ - (void)markerViewDidUpdate:(LuggMarkerView *)markerView { MKAnnotationView *annotationView = annotation.annotationView; if (annotationView) { + annotationView.draggable = markerView.draggable; annotationView.layer.zPosition = markerView.zIndex; annotationView.zPriority = markerView.zIndex; } diff --git a/ios/core/GoogleMapProvider.mm b/ios/core/GoogleMapProvider.mm index 67c4fca..b6a5630 100644 --- a/ios/core/GoogleMapProvider.mm +++ b/ios/core/GoogleMapProvider.mm @@ -304,6 +304,36 @@ - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { return NO; } +- (void)mapView:(GMSMapView *)mapView + didBeginDraggingMarker:(GMSMarker *)marker { + LuggMarkerView *markerView = [_markerToViewMap objectForKey:marker]; + if (markerView) { + [markerView updateCoordinate:marker.position]; + CGPoint point = [_mapView.projection pointForCoordinate:marker.position]; + [markerView emitDragStartEventWithPoint:point]; + } +} + +- (void)mapView:(GMSMapView *)mapView + didDragMarker:(GMSMarker *)marker { + LuggMarkerView *markerView = [_markerToViewMap objectForKey:marker]; + if (markerView) { + [markerView updateCoordinate:marker.position]; + CGPoint point = [_mapView.projection pointForCoordinate:marker.position]; + [markerView emitDragChangeEventWithPoint:point]; + } +} + +- (void)mapView:(GMSMapView *)mapView + didEndDraggingMarker:(GMSMarker *)marker { + LuggMarkerView *markerView = [_markerToViewMap objectForKey:marker]; + if (markerView) { + [markerView updateCoordinate:marker.position]; + CGPoint point = [_mapView.projection pointForCoordinate:marker.position]; + [markerView emitDragEndEventWithPoint:point]; + } +} + #pragma mark - MarkerViewDelegate - (void)markerViewDidLayout:(LuggMarkerView *)markerView { @@ -362,6 +392,7 @@ - (void)syncMarkerView:(LuggMarkerView *)markerView { marker.title = markerView.title; marker.snippet = markerView.markerDescription; marker.zIndex = (int)markerView.zIndex; + marker.draggable = markerView.draggable; [self applyMarkerStyle:markerView marker:marker]; } @@ -384,6 +415,7 @@ - (void)addMarkerViewToMap:(LuggMarkerView *)markerView { marker.title = markerView.title; marker.snippet = markerView.markerDescription; marker.zIndex = (int)markerView.zIndex; + marker.draggable = markerView.draggable; [self applyMarkerStyle:markerView marker:marker]; diff --git a/ios/events/MarkerDragEvent.h b/ios/events/MarkerDragEvent.h new file mode 100644 index 0000000..33f45ed --- /dev/null +++ b/ios/events/MarkerDragEvent.h @@ -0,0 +1,69 @@ +#pragma once + +#import + +namespace luggmaps { +namespace events { + +struct MarkerDragStartEvent { + double latitude; + double longitude; + double x; + double y; + + template + void emit(const facebook::react::SharedEventEmitter &eventEmitter) const { + if (!eventEmitter) + return; + auto emitter = std::static_pointer_cast(eventEmitter); + typename Emitter::OnMarkerDragStart event; + event.coordinate.latitude = latitude; + event.coordinate.longitude = longitude; + event.point.x = x; + event.point.y = y; + emitter->onMarkerDragStart(event); + } +}; + +struct MarkerDragChangeEvent { + double latitude; + double longitude; + double x; + double y; + + template + void emit(const facebook::react::SharedEventEmitter &eventEmitter) const { + if (!eventEmitter) + return; + auto emitter = std::static_pointer_cast(eventEmitter); + typename Emitter::OnMarkerDragChange event; + event.coordinate.latitude = latitude; + event.coordinate.longitude = longitude; + event.point.x = x; + event.point.y = y; + emitter->onMarkerDragChange(event); + } +}; + +struct MarkerDragEndEvent { + double latitude; + double longitude; + double x; + double y; + + template + void emit(const facebook::react::SharedEventEmitter &eventEmitter) const { + if (!eventEmitter) + return; + auto emitter = std::static_pointer_cast(eventEmitter); + typename Emitter::OnMarkerDragEnd event; + event.coordinate.latitude = latitude; + event.coordinate.longitude = longitude; + event.point.x = x; + event.point.y = y; + emitter->onMarkerDragEnd(event); + } +}; + +} // namespace events +} // namespace luggmaps diff --git a/src/components/Marker.tsx b/src/components/Marker.tsx index a5b545d..558cb19 100644 --- a/src/components/Marker.tsx +++ b/src/components/Marker.tsx @@ -5,6 +5,7 @@ import LuggMarkerViewNativeComponent from '../fabric/LuggMarkerViewNativeCompone import type { Coordinate, Point, PressEventPayload } from '../types'; export type MarkerPressEvent = NativeSyntheticEvent; +export type MarkerDragEvent = NativeSyntheticEvent; export interface MarkerProps { /** @@ -48,10 +49,27 @@ export interface MarkerProps { * @default true */ rasterize?: boolean; + /** + * Whether the marker can be dragged by the user. + * @default false + */ + draggable?: boolean; /** * Called when the marker is pressed */ onPress?: (event: MarkerPressEvent) => void; + /** + * Called when marker drag starts + */ + onDragStart?: (event: MarkerDragEvent) => void; + /** + * Called continuously as the marker is dragged + */ + onDragChange?: (event: MarkerDragEvent) => void; + /** + * Called when marker drag ends + */ + onDragEnd?: (event: MarkerDragEvent) => void; /** * Custom marker view */ @@ -82,7 +100,11 @@ export class Marker extends React.PureComponent { rotate = 0, scale = 1, rasterize = true, + draggable = false, onPress, + onDragStart, + onDragChange, + onDragEnd, children, } = this.props; @@ -97,7 +119,11 @@ export class Marker extends React.PureComponent { rotate={rotate} scale={scale} rasterize={rasterize} + draggable={draggable} onMarkerPress={onPress} + onMarkerDragStart={onDragStart} + onMarkerDragChange={onDragChange} + onMarkerDragEnd={onDragEnd} > {children} diff --git a/src/components/index.ts b/src/components/index.ts index 3783ad4..276c24b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,5 +1,5 @@ export { Marker } from './Marker'; -export type { MarkerProps, MarkerPressEvent } from './Marker'; +export type { MarkerProps, MarkerPressEvent, MarkerDragEvent } from './Marker'; export { Polygon } from './Polygon'; export type { PolygonProps } from './Polygon'; export { Polyline } from './Polyline'; diff --git a/src/fabric/LuggMarkerViewNativeComponent.ts b/src/fabric/LuggMarkerViewNativeComponent.ts index ac5929c..6b835a1 100644 --- a/src/fabric/LuggMarkerViewNativeComponent.ts +++ b/src/fabric/LuggMarkerViewNativeComponent.ts @@ -25,6 +25,7 @@ export interface NativeProps extends ViewProps { rotate?: WithDefault; scale?: WithDefault; rasterize?: WithDefault; + draggable?: WithDefault; onMarkerPress?: DirectEventHandler<{ coordinate: { latitude: Double; @@ -35,6 +36,36 @@ export interface NativeProps extends ViewProps { y: Double; }; }>; + onMarkerDragStart?: DirectEventHandler<{ + coordinate: { + latitude: Double; + longitude: Double; + }; + point: { + x: Double; + y: Double; + }; + }>; + onMarkerDragChange?: DirectEventHandler<{ + coordinate: { + latitude: Double; + longitude: Double; + }; + point: { + x: Double; + y: Double; + }; + }>; + onMarkerDragEnd?: DirectEventHandler<{ + coordinate: { + latitude: Double; + longitude: Double; + }; + point: { + x: Double; + y: Double; + }; + }>; } export default codegenNativeComponent( diff --git a/src/index.ts b/src/index.ts index b1f7846..8781e5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,11 @@ export { MapView } from './MapView'; export { MapProvider } from './MapProvider'; export type { MapProviderProps } from './MapProvider.types'; export { Marker } from './components'; -export type { MarkerProps, MarkerPressEvent } from './components'; +export type { + MarkerProps, + MarkerPressEvent, + MarkerDragEvent, +} from './components'; export { Polygon } from './components'; export type { PolygonProps } from './components'; export { Polyline } from './components';