@mmapp/react 0.1.0-alpha.1 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -0
- package/dist/index.d.mts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +70 -3
- package/dist/index.mjs +74 -12
- package/package.json +4 -3
- package/package.json.backup +0 -41
- package/src/Blueprint.ts +0 -216
- package/src/__tests__/Blueprint.test.ts +0 -106
- package/src/__tests__/action-context.test.ts +0 -166
- package/src/__tests__/actionCreators.test.ts +0 -179
- package/src/__tests__/builders.test.ts +0 -336
- package/src/__tests__/defineBlueprint-composition.test.ts +0 -106
- package/src/__tests__/factories.test.ts +0 -229
- package/src/__tests__/loader.test.ts +0 -159
- package/src/__tests__/logger.test.ts +0 -70
- package/src/__tests__/type-inference.test.ts +0 -160
- package/src/__tests__/typed-transitions.test.ts +0 -126
- package/src/__tests__/useModuleConfig.test.ts +0 -61
- package/src/actionCreators.ts +0 -132
- package/src/actions.ts +0 -547
- package/src/atoms/index.ts +0 -600
- package/src/authoring.ts +0 -92
- package/src/browser-player.ts +0 -783
- package/src/builders.ts +0 -1342
- package/src/components/ExperienceWorkflowBridge.tsx +0 -123
- package/src/components/PlayerProvider.tsx +0 -43
- package/src/components/atoms/index.tsx +0 -269
- package/src/components/index.ts +0 -36
- package/src/conditions.ts +0 -692
- package/src/config/defineBlueprint.ts +0 -329
- package/src/config/defineModel.ts +0 -753
- package/src/config/defineWorkspace.ts +0 -24
- package/src/core/WorkflowRuntime.ts +0 -153
- package/src/factories.ts +0 -425
- package/src/grammar/index.ts +0 -173
- package/src/hooks/index.ts +0 -106
- package/src/hooks/useAuth.ts +0 -288
- package/src/hooks/useChannel.ts +0 -304
- package/src/hooks/useComputed.ts +0 -154
- package/src/hooks/useDomainSubscription.ts +0 -110
- package/src/hooks/useDuringAction.ts +0 -99
- package/src/hooks/useExperienceState.ts +0 -59
- package/src/hooks/useExpressionLibrary.ts +0 -129
- package/src/hooks/useForm.ts +0 -352
- package/src/hooks/useGeolocation.ts +0 -207
- package/src/hooks/useMapView.ts +0 -259
- package/src/hooks/useMiddleware.ts +0 -291
- package/src/hooks/useModel.ts +0 -363
- package/src/hooks/useModule.ts +0 -59
- package/src/hooks/useModuleConfig.ts +0 -61
- package/src/hooks/useMutation.ts +0 -237
- package/src/hooks/useNotification.ts +0 -151
- package/src/hooks/useOnChange.ts +0 -30
- package/src/hooks/useOnEnter.ts +0 -59
- package/src/hooks/useOnEvent.ts +0 -37
- package/src/hooks/useOnExit.ts +0 -27
- package/src/hooks/useOnTransition.ts +0 -30
- package/src/hooks/usePackage.ts +0 -128
- package/src/hooks/useParams.ts +0 -33
- package/src/hooks/usePlayer.ts +0 -308
- package/src/hooks/useQuery.ts +0 -184
- package/src/hooks/useRealtimeQuery.ts +0 -222
- package/src/hooks/useRole.ts +0 -191
- package/src/hooks/useRouteParams.ts +0 -100
- package/src/hooks/useRouter.ts +0 -347
- package/src/hooks/useServerAction.ts +0 -178
- package/src/hooks/useServerState.ts +0 -284
- package/src/hooks/useToast.ts +0 -164
- package/src/hooks/useTransition.ts +0 -39
- package/src/hooks/useView.ts +0 -102
- package/src/hooks/useWhileIn.ts +0 -48
- package/src/hooks/useWorkflow.ts +0 -63
- package/src/index.ts +0 -465
- package/src/loader/experience-workflow-loader.ts +0 -192
- package/src/loader/index.ts +0 -6
- package/src/local/LocalEngine.ts +0 -388
- package/src/local/LocalEngineAdapter.ts +0 -175
- package/src/local/LocalEngineContext.ts +0 -30
- package/src/logger.ts +0 -37
- package/src/mixins.ts +0 -1160
- package/src/providers/RuntimeContext.ts +0 -20
- package/src/providers/WorkflowProvider.tsx +0 -28
- package/src/routing/instance-key.ts +0 -107
- package/src/server/transition-context.ts +0 -172
- package/src/testing/index.ts +0 -9
- package/src/testing/useBlueprintTestRunner.ts +0 -91
- package/src/testing/useGraphAnalysis.ts +0 -18
- package/src/testing/useTestRunner.ts +0 -77
- package/src/testing.ts +0 -995
- package/src/types/workflow-inference.ts +0 -158
- package/src/types.ts +0 -114
- package/tsconfig.json +0 -27
- package/vitest.config.ts +0 -8
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useGeolocation — GPS tracking hook for location-aware workflows.
|
|
3
|
-
*
|
|
4
|
-
* Provides real-time geolocation data using the browser's Geolocation API.
|
|
5
|
-
* Supports both one-shot position queries and continuous watch mode
|
|
6
|
-
* (critical for ride tracking, delivery, fleet management).
|
|
7
|
-
*
|
|
8
|
-
* Usage in .workflow.tsx:
|
|
9
|
-
* const { latitude, longitude, heading, speed, loading } = useGeolocation({
|
|
10
|
-
* enableHighAccuracy: true,
|
|
11
|
-
* watch: true,
|
|
12
|
-
* });
|
|
13
|
-
*
|
|
14
|
-
* // Publish location to ride channel
|
|
15
|
-
* useOnChange('latitude', (lat) => {
|
|
16
|
-
* channel.publish('location', { lat, lng: longitude });
|
|
17
|
-
* });
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
21
|
-
|
|
22
|
-
// =============================================================================
|
|
23
|
-
// Types
|
|
24
|
-
// =============================================================================
|
|
25
|
-
|
|
26
|
-
/** Geolocation position data. */
|
|
27
|
-
export interface GeoPosition {
|
|
28
|
-
/** Latitude in decimal degrees. */
|
|
29
|
-
latitude: number | null;
|
|
30
|
-
/** Longitude in decimal degrees. */
|
|
31
|
-
longitude: number | null;
|
|
32
|
-
/** Accuracy in meters. */
|
|
33
|
-
accuracy: number | null;
|
|
34
|
-
/** Altitude in meters (if available). */
|
|
35
|
-
altitude: number | null;
|
|
36
|
-
/** Altitude accuracy in meters (if available). */
|
|
37
|
-
altitudeAccuracy: number | null;
|
|
38
|
-
/** Heading in degrees from true north (if moving). */
|
|
39
|
-
heading: number | null;
|
|
40
|
-
/** Speed in meters per second (if moving). */
|
|
41
|
-
speed: number | null;
|
|
42
|
-
/** Timestamp of the position reading. */
|
|
43
|
-
timestamp: number | null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Options for configuring geolocation. */
|
|
47
|
-
export interface GeolocationOptions {
|
|
48
|
-
/** Use GPS for high-accuracy positions (uses more battery). */
|
|
49
|
-
enableHighAccuracy?: boolean;
|
|
50
|
-
/** Maximum age of cached position in ms (default: 0). */
|
|
51
|
-
maximumAge?: number;
|
|
52
|
-
/** Timeout for position request in ms (default: 10000). */
|
|
53
|
-
timeout?: number;
|
|
54
|
-
/** Continuously watch position changes (default: false). */
|
|
55
|
-
watch?: boolean;
|
|
56
|
-
/** Enable/disable tracking (default: true). */
|
|
57
|
-
enabled?: boolean;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Geolocation result returned by useGeolocation. */
|
|
61
|
-
export interface GeolocationResult extends GeoPosition {
|
|
62
|
-
/** Whether the initial position is still loading. */
|
|
63
|
-
loading: boolean;
|
|
64
|
-
/** Geolocation error (permission denied, unavailable, etc.). */
|
|
65
|
-
error: GeolocationError | null;
|
|
66
|
-
/** Whether the browser supports geolocation. */
|
|
67
|
-
supported: boolean;
|
|
68
|
-
/** Manually request current position. */
|
|
69
|
-
getCurrentPosition: () => void;
|
|
70
|
-
/** Start continuous watch (if not already watching). */
|
|
71
|
-
startWatching: () => void;
|
|
72
|
-
/** Stop continuous watch. */
|
|
73
|
-
stopWatching: () => void;
|
|
74
|
-
/** Whether currently watching position. */
|
|
75
|
-
watching: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Geolocation error with code. */
|
|
79
|
-
export interface GeolocationError {
|
|
80
|
-
/** Error code: 1=PERMISSION_DENIED, 2=POSITION_UNAVAILABLE, 3=TIMEOUT. */
|
|
81
|
-
code: number;
|
|
82
|
-
/** Human-readable error message. */
|
|
83
|
-
message: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// =============================================================================
|
|
87
|
-
// Hook
|
|
88
|
-
// =============================================================================
|
|
89
|
-
|
|
90
|
-
const INITIAL_POSITION: GeoPosition = {
|
|
91
|
-
latitude: null,
|
|
92
|
-
longitude: null,
|
|
93
|
-
accuracy: null,
|
|
94
|
-
altitude: null,
|
|
95
|
-
altitudeAccuracy: null,
|
|
96
|
-
heading: null,
|
|
97
|
-
speed: null,
|
|
98
|
-
timestamp: null,
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Real-time geolocation tracking via the browser Geolocation API.
|
|
103
|
-
*
|
|
104
|
-
* @param options - Geolocation configuration.
|
|
105
|
-
* @returns Position data with loading/error states and control methods.
|
|
106
|
-
*/
|
|
107
|
-
export function useGeolocation(options: GeolocationOptions = {}): GeolocationResult {
|
|
108
|
-
const {
|
|
109
|
-
enableHighAccuracy = false,
|
|
110
|
-
maximumAge = 0,
|
|
111
|
-
timeout = 10000,
|
|
112
|
-
watch = false,
|
|
113
|
-
enabled = true,
|
|
114
|
-
} = options;
|
|
115
|
-
|
|
116
|
-
const [position, setPosition] = useState<GeoPosition>(INITIAL_POSITION);
|
|
117
|
-
const [loading, setLoading] = useState(true);
|
|
118
|
-
const [error, setError] = useState<GeolocationError | null>(null);
|
|
119
|
-
const [watching, setWatching] = useState(false);
|
|
120
|
-
const watchIdRef = useRef<number | null>(null);
|
|
121
|
-
|
|
122
|
-
const supported = typeof navigator !== 'undefined' && 'geolocation' in navigator;
|
|
123
|
-
|
|
124
|
-
const positionOptions: PositionOptions = useMemo(
|
|
125
|
-
() => ({ enableHighAccuracy, maximumAge, timeout }),
|
|
126
|
-
[enableHighAccuracy, maximumAge, timeout],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const handleSuccess = useCallback((pos: GeolocationPosition) => {
|
|
130
|
-
setPosition({
|
|
131
|
-
latitude: pos.coords.latitude,
|
|
132
|
-
longitude: pos.coords.longitude,
|
|
133
|
-
accuracy: pos.coords.accuracy,
|
|
134
|
-
altitude: pos.coords.altitude,
|
|
135
|
-
altitudeAccuracy: pos.coords.altitudeAccuracy,
|
|
136
|
-
heading: pos.coords.heading,
|
|
137
|
-
speed: pos.coords.speed,
|
|
138
|
-
timestamp: pos.timestamp,
|
|
139
|
-
});
|
|
140
|
-
setError(null);
|
|
141
|
-
setLoading(false);
|
|
142
|
-
}, []);
|
|
143
|
-
|
|
144
|
-
const handleError = useCallback((err: GeolocationPositionError) => {
|
|
145
|
-
setError({ code: err.code, message: err.message });
|
|
146
|
-
setLoading(false);
|
|
147
|
-
}, []);
|
|
148
|
-
|
|
149
|
-
const getCurrentPosition = useCallback(() => {
|
|
150
|
-
if (!supported) {
|
|
151
|
-
setError({ code: 0, message: 'Geolocation is not supported' });
|
|
152
|
-
setLoading(false);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
setLoading(true);
|
|
156
|
-
navigator.geolocation.getCurrentPosition(handleSuccess, handleError, positionOptions);
|
|
157
|
-
}, [supported, handleSuccess, handleError, positionOptions]);
|
|
158
|
-
|
|
159
|
-
const startWatching = useCallback(() => {
|
|
160
|
-
if (!supported || watchIdRef.current !== null) return;
|
|
161
|
-
const id = navigator.geolocation.watchPosition(handleSuccess, handleError, positionOptions);
|
|
162
|
-
watchIdRef.current = id;
|
|
163
|
-
setWatching(true);
|
|
164
|
-
}, [supported, handleSuccess, handleError, positionOptions]);
|
|
165
|
-
|
|
166
|
-
const stopWatching = useCallback(() => {
|
|
167
|
-
if (watchIdRef.current !== null) {
|
|
168
|
-
navigator.geolocation.clearWatch(watchIdRef.current);
|
|
169
|
-
watchIdRef.current = null;
|
|
170
|
-
setWatching(false);
|
|
171
|
-
}
|
|
172
|
-
}, []);
|
|
173
|
-
|
|
174
|
-
// Auto-start on mount
|
|
175
|
-
useEffect(() => {
|
|
176
|
-
if (!enabled || !supported) {
|
|
177
|
-
if (!supported) {
|
|
178
|
-
setError({ code: 0, message: 'Geolocation is not supported' });
|
|
179
|
-
}
|
|
180
|
-
setLoading(false);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (watch) {
|
|
185
|
-
startWatching();
|
|
186
|
-
} else {
|
|
187
|
-
getCurrentPosition();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return () => {
|
|
191
|
-
stopWatching();
|
|
192
|
-
};
|
|
193
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
194
|
-
}, [enabled, watch, supported]);
|
|
195
|
-
|
|
196
|
-
const handle: GeolocationResult = {
|
|
197
|
-
...position,
|
|
198
|
-
loading,
|
|
199
|
-
error,
|
|
200
|
-
supported,
|
|
201
|
-
getCurrentPosition,
|
|
202
|
-
startWatching,
|
|
203
|
-
stopWatching,
|
|
204
|
-
watching,
|
|
205
|
-
};
|
|
206
|
-
return handle;
|
|
207
|
-
}
|
package/src/hooks/useMapView.ts
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useMapView — Map state management for location-based workflows.
|
|
3
|
-
*
|
|
4
|
-
* Manages map viewport state (center, zoom, bounds) independent of
|
|
5
|
-
* any specific map library. Works with Mapbox, Google Maps, Leaflet, etc.
|
|
6
|
-
*
|
|
7
|
-
* Usage in .workflow.tsx:
|
|
8
|
-
* const map = useMapView({
|
|
9
|
-
* initialCenter: { lat: 40.7128, lng: -74.0060 },
|
|
10
|
-
* initialZoom: 13,
|
|
11
|
-
* });
|
|
12
|
-
*
|
|
13
|
-
* // Track driver position
|
|
14
|
-
* useOnChange('driverLocation', (loc) => {
|
|
15
|
-
* map.panTo(loc.lat, loc.lng);
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* // Fit all markers in view
|
|
19
|
-
* map.fitBounds(markers.map(m => ({ lat: m.lat, lng: m.lng })));
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
23
|
-
|
|
24
|
-
// =============================================================================
|
|
25
|
-
// Types
|
|
26
|
-
// =============================================================================
|
|
27
|
-
|
|
28
|
-
/** Geographic coordinate. */
|
|
29
|
-
export interface LatLng {
|
|
30
|
-
lat: number;
|
|
31
|
-
lng: number;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Geographic bounds (rectangle). */
|
|
35
|
-
export interface LatLngBounds {
|
|
36
|
-
north: number;
|
|
37
|
-
south: number;
|
|
38
|
-
east: number;
|
|
39
|
-
west: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** A marker on the map. */
|
|
43
|
-
export interface MapMarker {
|
|
44
|
-
id: string;
|
|
45
|
-
position: LatLng;
|
|
46
|
-
label?: string;
|
|
47
|
-
icon?: string;
|
|
48
|
-
data?: Record<string, unknown>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Options for configuring the map view. */
|
|
52
|
-
export interface MapViewOptions {
|
|
53
|
-
/** Initial center coordinate. */
|
|
54
|
-
initialCenter?: LatLng;
|
|
55
|
-
/** Initial zoom level (0-22). */
|
|
56
|
-
initialZoom?: number;
|
|
57
|
-
/** Minimum zoom level. */
|
|
58
|
-
minZoom?: number;
|
|
59
|
-
/** Maximum zoom level. */
|
|
60
|
-
maxZoom?: number;
|
|
61
|
-
/** Restrict panning to these bounds. */
|
|
62
|
-
maxBounds?: LatLngBounds;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Map view handle returned by useMapView. */
|
|
66
|
-
export interface MapViewHandle {
|
|
67
|
-
/** Current center coordinate. */
|
|
68
|
-
center: LatLng;
|
|
69
|
-
/** Current zoom level. */
|
|
70
|
-
zoom: number;
|
|
71
|
-
/** Current viewport bounds. */
|
|
72
|
-
bounds: LatLngBounds | null;
|
|
73
|
-
/** Map markers. */
|
|
74
|
-
markers: MapMarker[];
|
|
75
|
-
/** Set the map center. */
|
|
76
|
-
setCenter: (lat: number, lng: number) => void;
|
|
77
|
-
/** Set the zoom level. */
|
|
78
|
-
setZoom: (zoom: number) => void;
|
|
79
|
-
/** Smoothly pan to a coordinate. */
|
|
80
|
-
panTo: (lat: number, lng: number) => void;
|
|
81
|
-
/** Adjust viewport to fit given coordinates. */
|
|
82
|
-
fitBounds: (points: LatLng[], padding?: number) => void;
|
|
83
|
-
/** Set viewport bounds (from map library callbacks). */
|
|
84
|
-
setBounds: (bounds: LatLngBounds) => void;
|
|
85
|
-
/** Add a marker. */
|
|
86
|
-
addMarker: (marker: MapMarker) => void;
|
|
87
|
-
/** Remove a marker by ID. */
|
|
88
|
-
removeMarker: (id: string) => void;
|
|
89
|
-
/** Update a marker's position. */
|
|
90
|
-
updateMarker: (id: string, position: LatLng) => void;
|
|
91
|
-
/** Clear all markers. */
|
|
92
|
-
clearMarkers: () => void;
|
|
93
|
-
/** Calculate distance between two points in meters. */
|
|
94
|
-
distance: (from: LatLng, to: LatLng) => number;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// =============================================================================
|
|
98
|
-
// Internal
|
|
99
|
-
// =============================================================================
|
|
100
|
-
|
|
101
|
-
/** Haversine distance in meters. */
|
|
102
|
-
function haversineDistance(from: LatLng, to: LatLng): number {
|
|
103
|
-
const R = 6371000; // Earth radius in meters
|
|
104
|
-
const dLat = ((to.lat - from.lat) * Math.PI) / 180;
|
|
105
|
-
const dLng = ((to.lng - from.lng) * Math.PI) / 180;
|
|
106
|
-
const a =
|
|
107
|
-
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
108
|
-
Math.cos((from.lat * Math.PI) / 180) *
|
|
109
|
-
Math.cos((to.lat * Math.PI) / 180) *
|
|
110
|
-
Math.sin(dLng / 2) *
|
|
111
|
-
Math.sin(dLng / 2);
|
|
112
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
113
|
-
return R * c;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Calculate bounds that contain all points. */
|
|
117
|
-
function boundsFromPoints(points: LatLng[], padding: number): LatLngBounds {
|
|
118
|
-
let north = -90, south = 90, east = -180, west = 180;
|
|
119
|
-
|
|
120
|
-
for (const p of points) {
|
|
121
|
-
if (p.lat > north) north = p.lat;
|
|
122
|
-
if (p.lat < south) south = p.lat;
|
|
123
|
-
if (p.lng > east) east = p.lng;
|
|
124
|
-
if (p.lng < west) west = p.lng;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Add padding (rough degree-based padding)
|
|
128
|
-
const latPad = (north - south) * padding;
|
|
129
|
-
const lngPad = (east - west) * padding;
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
north: north + latPad,
|
|
133
|
-
south: south - latPad,
|
|
134
|
-
east: east + lngPad,
|
|
135
|
-
west: west - lngPad,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Calculate center from bounds. */
|
|
140
|
-
function centerFromBounds(bounds: LatLngBounds): LatLng {
|
|
141
|
-
return {
|
|
142
|
-
lat: (bounds.north + bounds.south) / 2,
|
|
143
|
-
lng: (bounds.east + bounds.west) / 2,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Estimate zoom level from bounds. */
|
|
148
|
-
function zoomFromBounds(bounds: LatLngBounds): number {
|
|
149
|
-
const latDiff = bounds.north - bounds.south;
|
|
150
|
-
const lngDiff = bounds.east - bounds.west;
|
|
151
|
-
const maxDiff = Math.max(latDiff, lngDiff);
|
|
152
|
-
|
|
153
|
-
if (maxDiff >= 360) return 0;
|
|
154
|
-
// Rough estimate: each zoom level halves the view
|
|
155
|
-
return Math.max(0, Math.min(20, Math.round(Math.log2(360 / maxDiff))));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// =============================================================================
|
|
159
|
-
// Hook
|
|
160
|
-
// =============================================================================
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Map viewport state manager for location-based applications.
|
|
164
|
-
*
|
|
165
|
-
* @param options - Map configuration (center, zoom, bounds).
|
|
166
|
-
* @returns Map view handle with viewport state and control methods.
|
|
167
|
-
*/
|
|
168
|
-
export function useMapView(options: MapViewOptions = {}): MapViewHandle {
|
|
169
|
-
const {
|
|
170
|
-
initialCenter = { lat: 0, lng: 0 },
|
|
171
|
-
initialZoom = 10,
|
|
172
|
-
minZoom = 0,
|
|
173
|
-
maxZoom = 22,
|
|
174
|
-
} = options;
|
|
175
|
-
|
|
176
|
-
const [center, setCenterState] = useState<LatLng>(initialCenter);
|
|
177
|
-
const [zoom, setZoomState] = useState(initialZoom);
|
|
178
|
-
const [bounds, setBoundsState] = useState<LatLngBounds | null>(null);
|
|
179
|
-
const [markers, setMarkers] = useState<MapMarker[]>([]);
|
|
180
|
-
|
|
181
|
-
const setCenter = useCallback((lat: number, lng: number) => {
|
|
182
|
-
setCenterState({ lat, lng });
|
|
183
|
-
}, []);
|
|
184
|
-
|
|
185
|
-
const setZoom = useCallback(
|
|
186
|
-
(z: number) => {
|
|
187
|
-
setZoomState(Math.max(minZoom, Math.min(maxZoom, z)));
|
|
188
|
-
},
|
|
189
|
-
[minZoom, maxZoom],
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
const panTo = useCallback((lat: number, lng: number) => {
|
|
193
|
-
setCenterState({ lat, lng });
|
|
194
|
-
}, []);
|
|
195
|
-
|
|
196
|
-
const fitBounds = useCallback(
|
|
197
|
-
(points: LatLng[], padding = 0.1) => {
|
|
198
|
-
if (points.length === 0) return;
|
|
199
|
-
|
|
200
|
-
if (points.length === 1) {
|
|
201
|
-
setCenterState(points[0]);
|
|
202
|
-
setZoomState(15);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const calculatedBounds = boundsFromPoints(points, padding);
|
|
207
|
-
setCenterState(centerFromBounds(calculatedBounds));
|
|
208
|
-
setZoomState(Math.max(minZoom, Math.min(maxZoom, zoomFromBounds(calculatedBounds))));
|
|
209
|
-
setBoundsState(calculatedBounds);
|
|
210
|
-
},
|
|
211
|
-
[minZoom, maxZoom],
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const setBounds = useCallback((b: LatLngBounds) => {
|
|
215
|
-
setBoundsState(b);
|
|
216
|
-
}, []);
|
|
217
|
-
|
|
218
|
-
const addMarker = useCallback((marker: MapMarker) => {
|
|
219
|
-
setMarkers((prev) => [...prev.filter((m) => m.id !== marker.id), marker]);
|
|
220
|
-
}, []);
|
|
221
|
-
|
|
222
|
-
const removeMarker = useCallback((id: string) => {
|
|
223
|
-
setMarkers((prev) => prev.filter((m) => m.id !== id));
|
|
224
|
-
}, []);
|
|
225
|
-
|
|
226
|
-
const updateMarker = useCallback((id: string, position: LatLng) => {
|
|
227
|
-
setMarkers((prev) =>
|
|
228
|
-
prev.map((m) => (m.id === id ? { ...m, position } : m)),
|
|
229
|
-
);
|
|
230
|
-
}, []);
|
|
231
|
-
|
|
232
|
-
const clearMarkers = useCallback(() => {
|
|
233
|
-
setMarkers([]);
|
|
234
|
-
}, []);
|
|
235
|
-
|
|
236
|
-
const distance = useCallback((from: LatLng, to: LatLng) => {
|
|
237
|
-
return haversineDistance(from, to);
|
|
238
|
-
}, []);
|
|
239
|
-
|
|
240
|
-
return useMemo(
|
|
241
|
-
(): MapViewHandle => ({
|
|
242
|
-
center,
|
|
243
|
-
zoom,
|
|
244
|
-
bounds,
|
|
245
|
-
markers,
|
|
246
|
-
setCenter,
|
|
247
|
-
setZoom,
|
|
248
|
-
panTo,
|
|
249
|
-
fitBounds,
|
|
250
|
-
setBounds,
|
|
251
|
-
addMarker,
|
|
252
|
-
removeMarker,
|
|
253
|
-
updateMarker,
|
|
254
|
-
clearMarkers,
|
|
255
|
-
distance,
|
|
256
|
-
}),
|
|
257
|
-
[center, zoom, bounds, markers, setCenter, setZoom, panTo, fitBounds, setBounds, addMarker, removeMarker, updateMarker, clearMarkers, distance],
|
|
258
|
-
);
|
|
259
|
-
}
|