@mapfirst.ai/react 0.0.4
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/EXAMPLES.md +432 -0
- package/README.md +519 -0
- package/dist/index.d.mts +197 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# @mapfirst/react
|
|
2
|
+
|
|
3
|
+
React hooks for the MapFirst SDK supporting MapLibre, Google Maps, and Mapbox.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mapfirst/react @mapfirst/core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @mapfirst/react @mapfirst/core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @mapfirst/react @mapfirst/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
The React SDK supports a two-phase initialization pattern:
|
|
18
|
+
|
|
19
|
+
1. Create the MapFirst SDK instance (optionally with location data)
|
|
20
|
+
2. Attach your map when it's ready
|
|
21
|
+
|
|
22
|
+
### MapLibre GL JS
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
26
|
+
import maplibregl from "maplibre-gl";
|
|
27
|
+
import { useMapFirstCore, useMapLibreAttachment } from "@mapfirst/react";
|
|
28
|
+
import "maplibre-gl/dist/maplibre-gl.css";
|
|
29
|
+
|
|
30
|
+
function MapLibreExample() {
|
|
31
|
+
const mapContainerRef = useRef<HTMLDivElement>(null);
|
|
32
|
+
const [map, setMap] = useState<maplibregl.Map | null>(null);
|
|
33
|
+
|
|
34
|
+
// Phase 1: Create SDK instance with location data
|
|
35
|
+
const { mapFirst, state } = useMapFirstCore({
|
|
36
|
+
initialLocationData: {
|
|
37
|
+
city: "Paris",
|
|
38
|
+
country: "France",
|
|
39
|
+
currency: "EUR",
|
|
40
|
+
},
|
|
41
|
+
environment: "prod",
|
|
42
|
+
mfid: "your-mfid",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Access reactive state
|
|
46
|
+
const properties = state?.properties || [];
|
|
47
|
+
const isSearching = state?.isSearching || false;
|
|
48
|
+
|
|
49
|
+
// Initialize MapLibre map
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!mapContainerRef.current) return;
|
|
52
|
+
|
|
53
|
+
const mapInstance = new maplibregl.Map({
|
|
54
|
+
container: mapContainerRef.current,
|
|
55
|
+
style: "https://demotiles.maplibre.org/style.json",
|
|
56
|
+
center: [2.3522, 48.8566], // Paris
|
|
57
|
+
zoom: 12,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
mapInstance.on("load", () => {
|
|
61
|
+
setMap(mapInstance);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
mapInstance.remove();
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
// Phase 2: Attach map to SDK
|
|
70
|
+
useMapLibreAttachment({
|
|
71
|
+
mapFirst,
|
|
72
|
+
map,
|
|
73
|
+
maplibregl,
|
|
74
|
+
onMarkerClick: (marker) => {
|
|
75
|
+
console.log("Marker clicked:", marker);
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div ref={mapContainerRef} style={{ width: "100%", height: "600px" }} />
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Google Maps
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
89
|
+
import { useMapFirstCore, useGoogleMapsAttachment } from "@mapfirst/react";
|
|
90
|
+
|
|
91
|
+
function GoogleMapsExample() {
|
|
92
|
+
const mapContainerRef = useRef<HTMLDivElement>(null);
|
|
93
|
+
const [map, setMap] = useState<google.maps.Map | null>(null);
|
|
94
|
+
|
|
95
|
+
// Phase 1: Create SDK instance
|
|
96
|
+
const { mapFirst, state } = useMapFirstCore({
|
|
97
|
+
initialLocationData: {
|
|
98
|
+
city: "Tokyo",
|
|
99
|
+
country: "Japan",
|
|
100
|
+
currency: "JPY",
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Initialize Google Maps
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!mapContainerRef.current || !window.google) return;
|
|
107
|
+
|
|
108
|
+
const mapInstance = new google.maps.Map(mapContainerRef.current, {
|
|
109
|
+
center: { lat: 35.6762, lng: 139.6503 }, // Tokyo
|
|
110
|
+
zoom: 12,
|
|
111
|
+
mapId: "your-map-id", // Required for Advanced Markers
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
setMap(mapInstance);
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
117
|
+
// Phase 2: Attach map to SDK
|
|
118
|
+
useGoogleMapsAttachment({
|
|
119
|
+
mapFirst,
|
|
120
|
+
map,
|
|
121
|
+
google: window.google,
|
|
122
|
+
onMarkerClick: (marker) => {
|
|
123
|
+
console.log("Marker clicked:", marker);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div ref={mapContainerRef} style={{ width: "100%", height: "600px" }} />
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Mapbox GL JS
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
137
|
+
import mapboxgl from "mapbox-gl";
|
|
138
|
+
import { useMapFirstCore, useMapboxAttachment } from "@mapfirst/react";
|
|
139
|
+
import "mapbox-gl/dist/mapbox-gl.css";
|
|
140
|
+
|
|
141
|
+
function MapboxExample() {
|
|
142
|
+
const mapContainerRef = useRef<HTMLDivElement>(null);
|
|
143
|
+
const [map, setMap] = useState<mapboxgl.Map | null>(null);
|
|
144
|
+
|
|
145
|
+
// Phase 1: Create SDK instance
|
|
146
|
+
const { mapFirst, state } = useMapFirstCore({
|
|
147
|
+
initialLocationData: {
|
|
148
|
+
city: "London",
|
|
149
|
+
country: "United Kingdom",
|
|
150
|
+
currency: "GBP",
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Initialize Mapbox map
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!mapContainerRef.current) return;
|
|
157
|
+
|
|
158
|
+
mapboxgl.accessToken = "your-mapbox-token";
|
|
159
|
+
|
|
160
|
+
const mapInstance = new mapboxgl.Map({
|
|
161
|
+
container: mapContainerRef.current,
|
|
162
|
+
style: "mapbox://styles/mapbox/streets-v12",
|
|
163
|
+
center: [-0.1276, 51.5074], // London
|
|
164
|
+
zoom: 12,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
mapInstance.on("load", () => {
|
|
168
|
+
setMap(mapInstance);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return () => {
|
|
172
|
+
mapInstance.remove();
|
|
173
|
+
};
|
|
174
|
+
}, []);
|
|
175
|
+
|
|
176
|
+
// Phase 2: Attach map to SDK
|
|
177
|
+
useMapboxAttachment({
|
|
178
|
+
mapFirst,
|
|
179
|
+
map,
|
|
180
|
+
mapboxgl,
|
|
181
|
+
onMarkerClick: (marker) => {
|
|
182
|
+
console.log("Marker clicked:", marker);
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<div ref={mapContainerRef} style={{ width: "100%", height: "600px" }} />
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Advanced Usage
|
|
193
|
+
|
|
194
|
+
### Accessing SDK Methods and Reactive State
|
|
195
|
+
|
|
196
|
+
All SDK methods are available through the `mapFirst` instance, and state updates automatically trigger React re-renders:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
const { mapFirst, state } = useMapFirstCore({
|
|
200
|
+
/* ... */
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Access reactive state (automatically updates)
|
|
204
|
+
const properties = state?.properties || [];
|
|
205
|
+
const isSearching = state?.isSearching || false;
|
|
206
|
+
const selectedId = state?.selectedPropertyId;
|
|
207
|
+
const filters = state?.filters;
|
|
208
|
+
|
|
209
|
+
// Fly to location
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (mapFirst) {
|
|
212
|
+
mapFirst.flyMapTo(2.3522, 48.8566, 14); // lng, lat, zoom
|
|
213
|
+
}
|
|
214
|
+
}, [mapFirst]);
|
|
215
|
+
|
|
216
|
+
// Run search
|
|
217
|
+
const handleSearch = async () => {
|
|
218
|
+
if (!mapFirst) return;
|
|
219
|
+
|
|
220
|
+
await mapFirst.runPropertiesSearch({
|
|
221
|
+
body: {
|
|
222
|
+
city: "Paris",
|
|
223
|
+
country: "France",
|
|
224
|
+
filters: {
|
|
225
|
+
checkIn: "2024-06-01",
|
|
226
|
+
checkOut: "2024-06-07",
|
|
227
|
+
numAdults: 2,
|
|
228
|
+
currency: "EUR",
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Set filters
|
|
235
|
+
mapFirst?.setFilters({
|
|
236
|
+
checkIn: new Date("2024-06-01"),
|
|
237
|
+
checkOut: new Date("2024-06-07"),
|
|
238
|
+
numAdults: 2,
|
|
239
|
+
currency: "EUR",
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Alternative: Using Dedicated Hooks
|
|
244
|
+
|
|
245
|
+
For more granular control, use the dedicated hooks that only update when specific state changes:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
const { mapFirst } = useMapFirstCore({
|
|
249
|
+
/* ... */
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Only re-renders when properties change
|
|
253
|
+
const properties = useMapFirstProperties(mapFirst);
|
|
254
|
+
|
|
255
|
+
// Only re-renders when selection changes
|
|
256
|
+
const selectedId = useMapFirstSelectedProperty(mapFirst);
|
|
257
|
+
|
|
258
|
+
// Get and set primary type with a single hook
|
|
259
|
+
const [primaryType, setPrimaryType] = usePrimaryType(mapFirst);
|
|
260
|
+
|
|
261
|
+
// Get and set selected marker with a single hook
|
|
262
|
+
const [selectedMarker, setSelectedMarker] = useSelectedMarker(mapFirst);
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div>
|
|
266
|
+
<h2>Properties ({properties.length})</h2>
|
|
267
|
+
|
|
268
|
+
{/* Type selector */}
|
|
269
|
+
<select
|
|
270
|
+
value={primaryType}
|
|
271
|
+
onChange={(e) => setPrimaryType(e.target.value as PropertyType)}
|
|
272
|
+
>
|
|
273
|
+
<option value="Accommodation">Hotels</option>
|
|
274
|
+
<option value="Restaurant">Restaurants</option>
|
|
275
|
+
<option value="Attraction">Attractions</option>
|
|
276
|
+
</select>
|
|
277
|
+
|
|
278
|
+
{/* Selection controls */}
|
|
279
|
+
{selectedMarker && (
|
|
280
|
+
<div>
|
|
281
|
+
<p>Selected: {selectedMarker}</p>
|
|
282
|
+
<button onClick={() => setSelectedMarker(null)}>Clear Selection</button>
|
|
283
|
+
</div>
|
|
284
|
+
)}
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### State Management with Callbacks
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
const mapFirst = useMapFirstCore({
|
|
293
|
+
initialLocationData: {
|
|
294
|
+
city: "Paris",
|
|
295
|
+
country: "France",
|
|
296
|
+
},
|
|
297
|
+
callbacks: {
|
|
298
|
+
onPropertiesChange: (properties) => {
|
|
299
|
+
console.log("Properties updated:", properties.length);
|
|
300
|
+
},
|
|
301
|
+
onSelectedPropertyChange: (id) => {
|
|
302
|
+
console.log("Selected property:", id);
|
|
303
|
+
},
|
|
304
|
+
onError: (error, context) => {
|
|
305
|
+
console.error(`Error in ${context}:`, error);
|
|
306
|
+
},
|
|
307
|
+
onLoadingStateChange: (loading) => {
|
|
308
|
+
console.log("Loading:", loading);
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Configuring Map Behavior
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
const mapFirst = useMapFirstCore({
|
|
318
|
+
initialLocationData: {
|
|
319
|
+
city: "New York",
|
|
320
|
+
country: "United States",
|
|
321
|
+
},
|
|
322
|
+
fitBoundsPadding: {
|
|
323
|
+
top: 100,
|
|
324
|
+
bottom: 200,
|
|
325
|
+
left: 50,
|
|
326
|
+
right: 50,
|
|
327
|
+
},
|
|
328
|
+
properties: [], // Initial properties
|
|
329
|
+
primaryType: "Accommodation", // or 'Restaurant', 'Attraction'
|
|
330
|
+
autoSelectOnClick: true, // Auto-select marker on click
|
|
331
|
+
environment: "prod", // or 'test'
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## API Reference
|
|
336
|
+
|
|
337
|
+
### `useMapFirstCore(options)`
|
|
338
|
+
|
|
339
|
+
Creates a MapFirst SDK instance and returns reactive state.
|
|
340
|
+
|
|
341
|
+
**Returns:** `{ mapFirst: MapFirstCore | null, state: MapState | null }`
|
|
342
|
+
|
|
343
|
+
- `mapFirst` - The SDK instance for calling methods
|
|
344
|
+
- `state` - Reactive state that updates when SDK state changes
|
|
345
|
+
- `properties` - Array of properties
|
|
346
|
+
- `selectedPropertyId` - Currently selected property ID
|
|
347
|
+
- `primary` - Primary property type
|
|
348
|
+
- `filters` - Current filter state
|
|
349
|
+
- `isSearching` - Whether a search is in progress
|
|
350
|
+
- `initialLoading` - Whether initial data is loading
|
|
351
|
+
- `bounds` - Current map bounds
|
|
352
|
+
- `center` - Map center coordinates
|
|
353
|
+
- `zoom` - Current zoom level
|
|
354
|
+
- `activeLocation` - Active location data
|
|
355
|
+
|
|
356
|
+
**Options:**
|
|
357
|
+
|
|
358
|
+
- `initialLocationData?` - Location data for geo-lookup
|
|
359
|
+
- `city?` - City name
|
|
360
|
+
- `country?` - Country name
|
|
361
|
+
- `query?` - Search query
|
|
362
|
+
- `currency?` - Currency code
|
|
363
|
+
- `environment?` - API environment ('prod' | 'test')
|
|
364
|
+
- `mfid?` - MapFirst ID
|
|
365
|
+
- `state?` - Initial map state
|
|
366
|
+
- `callbacks?` - State change callbacks
|
|
367
|
+
- `fitBoundsPadding?` - Padding for fitBounds operations
|
|
368
|
+
- `properties?` - Initial properties
|
|
369
|
+
- `primaryType?` - Primary property type
|
|
370
|
+
- `autoSelectOnClick?` - Auto-select markers on click
|
|
371
|
+
|
|
372
|
+
### `useMapLibreAttachment(options)`
|
|
373
|
+
|
|
374
|
+
Attaches MapLibre map to SDK instance.
|
|
375
|
+
|
|
376
|
+
**Options:**
|
|
377
|
+
|
|
378
|
+
- `mapFirst` - SDK instance
|
|
379
|
+
- `map` - MapLibre map instance
|
|
380
|
+
- `maplibregl` - MapLibre GL namespace
|
|
381
|
+
- `onMarkerClick?` - Marker click handler
|
|
382
|
+
|
|
383
|
+
### `useGoogleMapsAttachment(options)`
|
|
384
|
+
|
|
385
|
+
Attaches Google Maps to SDK instance.
|
|
386
|
+
|
|
387
|
+
**Options:**
|
|
388
|
+
|
|
389
|
+
- `mapFirst` - SDK instance
|
|
390
|
+
- `map` - Google Maps instance
|
|
391
|
+
- `google` - Google Maps namespace
|
|
392
|
+
- `onMarkerClick?` - Marker click handler
|
|
393
|
+
|
|
394
|
+
### `useMapboxAttachment(options)`
|
|
395
|
+
|
|
396
|
+
Attaches Mapbox map to SDK instance.
|
|
397
|
+
|
|
398
|
+
**Options:**
|
|
399
|
+
|
|
400
|
+
- `mapFirst` - SDK instance
|
|
401
|
+
- `map` - Mapbox map instance
|
|
402
|
+
- `mapboxgl` - Mapbox GL namespace
|
|
403
|
+
- `onMarkerClick?` - Marker click handler
|
|
404
|
+
|
|
405
|
+
### `useMapFirstProperties(mapFirst)`
|
|
406
|
+
|
|
407
|
+
Returns the current properties array. Only triggers re-renders when properties change.
|
|
408
|
+
|
|
409
|
+
**Parameters:**
|
|
410
|
+
|
|
411
|
+
- `mapFirst` - SDK instance from `useMapFirstCore`
|
|
412
|
+
|
|
413
|
+
**Returns:** `Property[]`
|
|
414
|
+
|
|
415
|
+
**Example:**
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
const { mapFirst } = useMapFirstCore({ ... });
|
|
419
|
+
const properties = useMapFirstProperties(mapFirst);
|
|
420
|
+
|
|
421
|
+
return <div>Found {properties.length} properties</div>;
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### `useMapFirstSelectedProperty(mapFirst)`
|
|
425
|
+
|
|
426
|
+
Returns the currently selected property ID. Only triggers re-renders when selection changes.
|
|
427
|
+
|
|
428
|
+
**Parameters:**
|
|
429
|
+
|
|
430
|
+
- `mapFirst` - SDK instance from `useMapFirstCore`
|
|
431
|
+
|
|
432
|
+
**Returns:** `number | null`
|
|
433
|
+
|
|
434
|
+
**Example:**
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
const { mapFirst } = useMapFirstCore({ ... });
|
|
438
|
+
const selectedId = useMapFirstSelectedProperty(mapFirst);
|
|
439
|
+
|
|
440
|
+
return <div>Selected: {selectedId || 'None'}</div>;
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### `usePrimaryType(mapFirst)`
|
|
444
|
+
|
|
445
|
+
Returns the current primary property type and a setter function. Re-renders when primary type changes.
|
|
446
|
+
|
|
447
|
+
**Parameters:**
|
|
448
|
+
|
|
449
|
+
- `mapFirst` - SDK instance from `useMapFirstCore`
|
|
450
|
+
|
|
451
|
+
**Returns:** `[PropertyType, (type: PropertyType) => void]`
|
|
452
|
+
|
|
453
|
+
**Example:**
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
const { mapFirst } = useMapFirstCore({ ... });
|
|
457
|
+
const [primaryType, setPrimaryType] = usePrimaryType(mapFirst);
|
|
458
|
+
|
|
459
|
+
return (
|
|
460
|
+
<select value={primaryType} onChange={(e) => setPrimaryType(e.target.value as PropertyType)}>
|
|
461
|
+
<option value="Accommodation">Hotels</option>
|
|
462
|
+
<option value="Restaurant">Restaurants</option>
|
|
463
|
+
<option value="Attraction">Attractions</option>
|
|
464
|
+
</select>
|
|
465
|
+
);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### `useSelectedMarker(mapFirst)`
|
|
469
|
+
|
|
470
|
+
Returns the currently selected marker ID and a setter function. Re-renders when selection changes.
|
|
471
|
+
|
|
472
|
+
**Parameters:**
|
|
473
|
+
|
|
474
|
+
- `mapFirst` - SDK instance from `useMapFirstCore`
|
|
475
|
+
|
|
476
|
+
**Returns:** `[number | null, (id: number | null) => void]`
|
|
477
|
+
|
|
478
|
+
**Example:**
|
|
479
|
+
|
|
480
|
+
```tsx
|
|
481
|
+
const { mapFirst } = useMapFirstCore({ ... });
|
|
482
|
+
const [selectedMarker, setSelectedMarker] = useSelectedMarker(mapFirst);
|
|
483
|
+
|
|
484
|
+
return (
|
|
485
|
+
<div>
|
|
486
|
+
<p>Selected: {selectedMarker || 'None'}</p>
|
|
487
|
+
<button onClick={() => setSelectedMarker(null)}>Clear Selection</button>
|
|
488
|
+
<button onClick={() => setSelectedMarker(123456)}>Select Property 123456</button>
|
|
489
|
+
</div>
|
|
490
|
+
);
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Example:**
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
const { mapFirst } = useMapFirstCore({ ... });
|
|
497
|
+
const selectedId = useMapFirstSelectedProperty(mapFirst);
|
|
498
|
+
|
|
499
|
+
return <div>Selected: {selectedId || 'None'}</div>;
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## Legacy API
|
|
503
|
+
|
|
504
|
+
The old `useMapFirst` hook is still available but deprecated:
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
const mapFirst = useMapFirst({
|
|
508
|
+
platform: "maplibre",
|
|
509
|
+
mapInstance: mapLibreInstance,
|
|
510
|
+
maplibregl: maplibregl,
|
|
511
|
+
// ... other options
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Note:** This requires the map to be available immediately. Use the new two-phase initialization pattern instead.
|
|
516
|
+
|
|
517
|
+
## License
|
|
518
|
+
|
|
519
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { BaseMapFirstOptions, MapFirstCore, MapState, Property, PropertyType, MapLibreNamespace, GoogleMapsNamespace, MapboxNamespace, MapFirstOptions } from '@mapfirst.ai/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook that creates a MapFirstCore instance that can be initialized before maps are ready.
|
|
7
|
+
* Supports two-phase initialization: create SDK first, attach map later.
|
|
8
|
+
* Returns the instance and reactive state that updates when SDK state changes.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // Phase 1: Create SDK instance with location data
|
|
13
|
+
* const { mapFirst, state } = useMapFirstCore({
|
|
14
|
+
* initialLocationData: {
|
|
15
|
+
* city: "New York",
|
|
16
|
+
* country: "United States",
|
|
17
|
+
* currency: "USD"
|
|
18
|
+
* }
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Access reactive state
|
|
22
|
+
* console.log(state.properties); // Updates when properties change
|
|
23
|
+
* console.log(state.isSearching); // Updates when search state changes
|
|
24
|
+
*
|
|
25
|
+
* // Phase 2: Attach map when ready
|
|
26
|
+
* useEffect(() => {
|
|
27
|
+
* if (mapLibreInstance && mapFirst) {
|
|
28
|
+
* mapFirst.attachMap(mapLibreInstance, {
|
|
29
|
+
* platform: "maplibre",
|
|
30
|
+
* maplibregl: maplibregl,
|
|
31
|
+
* onMarkerClick: (marker) => console.log(marker)
|
|
32
|
+
* });
|
|
33
|
+
* }
|
|
34
|
+
* }, [mapLibreInstance, mapFirst]);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function useMapFirstCore(options: BaseMapFirstOptions): {
|
|
38
|
+
mapFirst: MapFirstCore | null;
|
|
39
|
+
state: MapState | null;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Hook to access reactive properties from MapFirst SDK.
|
|
43
|
+
* Returns the current properties array that updates when properties change.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const { mapFirst } = useMapFirstCore({ ... });
|
|
48
|
+
* const properties = useMapFirstProperties(mapFirst);
|
|
49
|
+
*
|
|
50
|
+
* return <div>Found {properties.length} properties</div>;
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function useMapFirstProperties(mapFirst: MapFirstCore | null): Property[];
|
|
54
|
+
/**
|
|
55
|
+
* Hook to access the selected property ID from MapFirst SDK.
|
|
56
|
+
* Returns the currently selected property ID that updates when selection changes.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* const { mapFirst } = useMapFirstCore({ ... });
|
|
61
|
+
* const selectedId = useMapFirstSelectedProperty(mapFirst);
|
|
62
|
+
*
|
|
63
|
+
* return <div>Selected: {selectedId || 'None'}</div>;
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare function useMapFirstSelectedProperty(mapFirst: MapFirstCore | null): number | null;
|
|
67
|
+
/**
|
|
68
|
+
* Hook to access and control the primary property type.
|
|
69
|
+
* Returns the current primary type and a setter function.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* const { mapFirst } = useMapFirstCore({ ... });
|
|
74
|
+
* const [primaryType, setPrimaryType] = usePrimaryType(mapFirst);
|
|
75
|
+
*
|
|
76
|
+
* return (
|
|
77
|
+
* <select value={primaryType} onChange={(e) => setPrimaryType(e.target.value as PropertyType)}>
|
|
78
|
+
* <option value="Accommodation">Hotels</option>
|
|
79
|
+
* <option value="Restaurant">Restaurants</option>
|
|
80
|
+
* <option value="Attraction">Attractions</option>
|
|
81
|
+
* </select>
|
|
82
|
+
* );
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function usePrimaryType(mapFirst: MapFirstCore | null): [PropertyType, (type: PropertyType) => void];
|
|
86
|
+
/**
|
|
87
|
+
* Hook to access and control the selected marker.
|
|
88
|
+
* Returns the current selected marker ID and a setter function.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```tsx
|
|
92
|
+
* const { mapFirst } = useMapFirstCore({ ... });
|
|
93
|
+
* const [selectedMarker, setSelectedMarker] = useSelectedMarker(mapFirst);
|
|
94
|
+
*
|
|
95
|
+
* return (
|
|
96
|
+
* <div>
|
|
97
|
+
* <p>Selected: {selectedMarker || 'None'}</p>
|
|
98
|
+
* <button onClick={() => setSelectedMarker(null)}>Clear Selection</button>
|
|
99
|
+
* </div>
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function useSelectedMarker(mapFirst: MapFirstCore | null): [number | null, (id: number | null) => void];
|
|
104
|
+
/**
|
|
105
|
+
* Hook for MapLibre GL JS integration.
|
|
106
|
+
* Automatically attaches the map when both the SDK instance and map are available.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: "Paris", country: "France" } });
|
|
111
|
+
* const mapRef = useRef<maplibregl.Map | null>(null);
|
|
112
|
+
*
|
|
113
|
+
* useMapLibreAttachment({
|
|
114
|
+
* mapFirst,
|
|
115
|
+
* map: mapRef.current,
|
|
116
|
+
* maplibregl: maplibregl,
|
|
117
|
+
* onMarkerClick: (marker) => console.log(marker)
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* // Access reactive state
|
|
121
|
+
* console.log(state?.properties);
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
declare function useMapLibreAttachment({ mapFirst, map, maplibregl, onMarkerClick, }: {
|
|
125
|
+
mapFirst: MapFirstCore | null;
|
|
126
|
+
map: any | null;
|
|
127
|
+
maplibregl: MapLibreNamespace;
|
|
128
|
+
onMarkerClick?: (marker: Property) => void;
|
|
129
|
+
}): void;
|
|
130
|
+
/**
|
|
131
|
+
* Hook for Google Maps integration.
|
|
132
|
+
* Automatically attaches the map when both the SDK instance and map are available.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```tsx
|
|
136
|
+
* const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: "Tokyo", country: "Japan" } });
|
|
137
|
+
* const mapRef = useRef<google.maps.Map | null>(null);
|
|
138
|
+
*
|
|
139
|
+
* useGoogleMapsAttachment({
|
|
140
|
+
* mapFirst,
|
|
141
|
+
* map: mapRef.current,
|
|
142
|
+
* google: window.google,
|
|
143
|
+
* onMarkerClick: (marker) => console.log(marker)
|
|
144
|
+
* });
|
|
145
|
+
*
|
|
146
|
+
* // Access reactive state
|
|
147
|
+
* console.log(state?.isSearching);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare function useGoogleMapsAttachment({ mapFirst, map, google, onMarkerClick, }: {
|
|
151
|
+
mapFirst: MapFirstCore | null;
|
|
152
|
+
map: any | null;
|
|
153
|
+
google: GoogleMapsNamespace;
|
|
154
|
+
onMarkerClick?: (marker: Property) => void;
|
|
155
|
+
}): void;
|
|
156
|
+
/**
|
|
157
|
+
* Hook for Mapbox GL JS integration.
|
|
158
|
+
* Automatically attaches the map when both the SDK instance and map are available.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```tsx
|
|
162
|
+
* const { mapFirst, state } = useMapFirstCore({ initialLocationData: { city: "London", country: "United Kingdom" } });
|
|
163
|
+
* const mapRef = useRef<mapboxgl.Map | null>(null);
|
|
164
|
+
*
|
|
165
|
+
* useMapboxAttachment({
|
|
166
|
+
* mapFirst,
|
|
167
|
+
* map: mapRef.current,
|
|
168
|
+
* mapboxgl: mapboxgl,
|
|
169
|
+
* onMarkerClick: (marker) => console.log(marker)
|
|
170
|
+
* });
|
|
171
|
+
*
|
|
172
|
+
* // Access reactive state
|
|
173
|
+
* console.log(state?.filters);
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function useMapboxAttachment({ mapFirst, map, mapboxgl, onMarkerClick, }: {
|
|
177
|
+
mapFirst: MapFirstCore | null;
|
|
178
|
+
map: any | null;
|
|
179
|
+
mapboxgl: MapboxNamespace;
|
|
180
|
+
onMarkerClick?: (marker: Property) => void;
|
|
181
|
+
}): void;
|
|
182
|
+
/**
|
|
183
|
+
* Legacy hook that creates the MapFirstCore instance with a map immediately.
|
|
184
|
+
* Use useMapFirstCore + useMap*Attachment hooks for better control.
|
|
185
|
+
*
|
|
186
|
+
* @deprecated Use useMapFirstCore and platform-specific attachment hooks instead
|
|
187
|
+
*/
|
|
188
|
+
declare function useMapFirst(options: MapFirstOptions | null): React.RefObject<MapFirstCore | null>;
|
|
189
|
+
/**
|
|
190
|
+
* Helper component that simply renders the markers it receives so non-React environments
|
|
191
|
+
* can verify data flows before wiring the SDK into a map.
|
|
192
|
+
*/
|
|
193
|
+
declare function MarkerDebugList({ markers }: {
|
|
194
|
+
markers: Property[];
|
|
195
|
+
}): react_jsx_runtime.JSX.Element;
|
|
196
|
+
|
|
197
|
+
export { MarkerDebugList, useGoogleMapsAttachment, useMapFirst, useMapFirstCore, useMapFirstProperties, useMapFirstSelectedProperty, useMapLibreAttachment, useMapboxAttachment, usePrimaryType, useSelectedMarker };
|