@object-ui/plugin-map 2.0.0 → 3.0.0

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.
@@ -5,6 +5,7 @@ export interface ObjectMapProps {
5
5
  dataSource?: DataSource;
6
6
  className?: string;
7
7
  onMarkerClick?: (record: any) => void;
8
+ onRowClick?: (record: any) => void;
8
9
  onEdit?: (record: any) => void;
9
10
  onDelete?: (record: any) => void;
10
11
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectMap.d.ts","sourceRoot":"","sources":["../../src/ObjectMap.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAI/E,OAAO,kCAAkC,CAAC;AAY1C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAsKD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAsQ9C,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ObjectMap.d.ts","sourceRoot":"","sources":["../../src/ObjectMap.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAuC,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAM/E,OAAO,kCAAkC,CAAC;AAY1C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAsKD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAsT9C,CAAC;AAEF,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-map",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Map visualization plugin for Object UI",
@@ -24,24 +24,24 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@objectstack/spec": "^2.0.7",
27
+ "@objectstack/spec": "^3.0.2",
28
28
  "lucide-react": "^0.563.0",
29
- "maplibre-gl": "^5.17.0",
29
+ "maplibre-gl": "^5.18.0",
30
30
  "react-map-gl": "^8.1.0",
31
31
  "zod": "^4.3.6",
32
- "@object-ui/components": "2.0.0",
33
- "@object-ui/core": "2.0.0",
34
- "@object-ui/react": "2.0.0",
35
- "@object-ui/types": "2.0.0"
32
+ "@object-ui/components": "3.0.0",
33
+ "@object-ui/react": "3.0.0",
34
+ "@object-ui/core": "3.0.0",
35
+ "@object-ui/types": "3.0.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": "^18.0.0 || ^19.0.0",
39
39
  "react-dom": "^18.0.0 || ^19.0.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/react": "^19.2.13",
43
- "@types/react-dom": "^19.2.3",
44
- "@vitejs/plugin-react": "^5.1.3",
42
+ "@types/react": "19.2.13",
43
+ "@types/react-dom": "19.2.3",
44
+ "@vitejs/plugin-react": "^5.1.4",
45
45
  "typescript": "^5.9.3",
46
46
  "vite": "^7.3.1",
47
47
  "vite-plugin-dts": "^4.5.4"
package/src/ObjectMap.tsx CHANGED
@@ -22,6 +22,8 @@
22
22
 
23
23
  import React, { useEffect, useState, useMemo } from 'react';
24
24
  import type { ObjectGridSchema, DataSource, ViewData } from '@object-ui/types';
25
+ import { useNavigationOverlay } from '@object-ui/react';
26
+ import { NavigationOverlay } from '@object-ui/components';
25
27
  import { z } from 'zod';
26
28
  import Map, { NavigationControl, Marker, Popup } from 'react-map-gl/maplibre';
27
29
  import maplibregl from 'maplibre-gl';
@@ -42,6 +44,7 @@ export interface ObjectMapProps {
42
44
  dataSource?: DataSource;
43
45
  className?: string;
44
46
  onMarkerClick?: (record: any) => void;
47
+ onRowClick?: (record: any) => void;
45
48
  onEdit?: (record: any) => void;
46
49
  onDelete?: (record: any) => void;
47
50
  }
@@ -215,6 +218,7 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
215
218
  dataSource,
216
219
  className,
217
220
  onMarkerClick,
221
+ onRowClick,
218
222
  onEdit,
219
223
  onDelete,
220
224
  ...rest
@@ -224,6 +228,7 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
224
228
  const [error, setError] = useState<Error | null>(null);
225
229
  const [objectSchema, setObjectSchema] = useState<any>(null);
226
230
  const [selectedMarkerId, setSelectedMarkerId] = useState<string | null>(null);
231
+ const [searchQuery, setSearchQuery] = useState('');
227
232
 
228
233
  const rawDataConfig = getDataConfig(schema);
229
234
  // Memoize dataConfig using deep comparison to prevent infinite loops
@@ -283,8 +288,8 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
283
288
  } else if (result && typeof result === 'object') {
284
289
  if (Array.isArray((result as any).data)) {
285
290
  items = (result as any).data;
286
- } else if (Array.isArray((result as any).value)) {
287
- items = (result as any).value;
291
+ } else if (Array.isArray((result as any).records)) {
292
+ items = (result as any).records;
288
293
  }
289
294
  }
290
295
  setData(items);
@@ -365,9 +370,24 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
365
370
  markers.find(m => m.id === selectedMarkerId),
366
371
  [markers, selectedMarkerId]);
367
372
 
373
+ const navigation = useNavigationOverlay({
374
+ navigation: (schema as any).navigation,
375
+ objectName: schema.objectName,
376
+ onRowClick,
377
+ });
378
+
379
+ const filteredMarkers = useMemo(() => {
380
+ if (!searchQuery.trim()) return markers;
381
+ const q = searchQuery.toLowerCase();
382
+ return markers.filter(m =>
383
+ m.title?.toLowerCase().includes(q) ||
384
+ m.description?.toLowerCase().includes(q)
385
+ );
386
+ }, [markers, searchQuery]);
387
+
368
388
  // Calculate map bounds
369
389
  const initialViewState = useMemo(() => {
370
- if (!markers.length) {
390
+ if (!filteredMarkers.length) {
371
391
  return {
372
392
  longitude: mapConfig.center?.[1] || 0,
373
393
  latitude: mapConfig.center?.[0] || 0,
@@ -376,8 +396,8 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
376
396
  }
377
397
 
378
398
  // Simple bounds calculation
379
- const lngs = markers.map(m => m.coordinates[0]);
380
- const lats = markers.map(m => m.coordinates[1]);
399
+ const lngs = filteredMarkers.map(m => m.coordinates[0]);
400
+ const lats = filteredMarkers.map(m => m.coordinates[1]);
381
401
 
382
402
  const minLng = Math.min(...lngs);
383
403
  const maxLng = Math.max(...lngs);
@@ -389,7 +409,7 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
389
409
  latitude: (minLat + maxLat) / 2,
390
410
  zoom: mapConfig.zoom || 3, // Auto-zoom logic could be improved here
391
411
  };
392
- }, [markers, mapConfig]);
412
+ }, [filteredMarkers, mapConfig]);
393
413
 
394
414
  if (loading) {
395
415
  return (
@@ -418,15 +438,29 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
418
438
  {`${invalidCount} record${invalidCount !== 1 ? 's' : ''} with missing or invalid coordinates excluded from the map.`}
419
439
  </div>
420
440
  )}
421
- <div className="relative border rounded-lg overflow-hidden bg-muted" style={{ height: '600px', width: '100%' }}>
441
+ {markers.length > 0 && (
442
+ <div className="mb-2">
443
+ <input
444
+ type="text"
445
+ value={searchQuery}
446
+ onChange={(e) => setSearchQuery(e.target.value)}
447
+ placeholder="Search locations…"
448
+ className="w-full px-3 py-2 text-sm border rounded-md bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
449
+ />
450
+ </div>
451
+ )}
452
+ <div className="relative border rounded-lg overflow-hidden bg-muted h-[300px] sm:h-[400px] md:h-[500px] lg:h-[600px] w-full">
422
453
  <Map
423
454
  initialViewState={initialViewState}
424
455
  style={{ width: '100%', height: '100%' }}
425
456
  mapStyle="https://demotiles.maplibre.org/style.json"
457
+ touchZoomRotate={true}
458
+ dragRotate={true}
459
+ touchPitch={true}
426
460
  >
427
- <NavigationControl position="top-right" />
461
+ <NavigationControl position="top-right" showCompass={true} showZoom={true} />
428
462
 
429
- {markers.map(marker => (
463
+ {filteredMarkers.map(marker => (
430
464
  <Marker
431
465
  key={marker.id}
432
466
  longitude={marker.coordinates[0]}
@@ -435,6 +469,7 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
435
469
  onClick={(e) => {
436
470
  e.originalEvent.stopPropagation();
437
471
  setSelectedMarkerId(marker.id);
472
+ navigation.handleClick(marker.data);
438
473
  onMarkerClick?.(marker.data);
439
474
  }}
440
475
  >
@@ -452,7 +487,7 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
452
487
  onClose={() => setSelectedMarkerId(null)}
453
488
  closeOnClick={false}
454
489
  >
455
- <div className="p-2 min-w-[200px]">
490
+ <div className="p-2 min-w-[150px] sm:min-w-[200px]">
456
491
  <h3 className="font-bold text-sm mb-1">{selectedMarker.title}</h3>
457
492
  {selectedMarker.description && (
458
493
  <p className="text-xs text-muted-foreground">{selectedMarker.description}</p>
@@ -470,6 +505,22 @@ export const ObjectMap: React.FC<ObjectMapProps> = ({
470
505
  )}
471
506
  </Map>
472
507
  </div>
508
+ {navigation.isOverlay && (
509
+ <NavigationOverlay {...navigation} title="Location Details">
510
+ {(record) => (
511
+ <div className="space-y-3">
512
+ {Object.entries(record).map(([key, value]) => (
513
+ <div key={key} className="flex flex-col">
514
+ <span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
515
+ {key.replace(/_/g, ' ')}
516
+ </span>
517
+ <span className="text-sm">{String(value ?? '—')}</span>
518
+ </div>
519
+ ))}
520
+ </div>
521
+ )}
522
+ </NavigationOverlay>
523
+ )}
473
524
  </div>
474
525
  );
475
526
  };