@dxos/react-ui-geo 0.8.3 → 0.8.4-main.28f8d3d

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.
Files changed (83) hide show
  1. package/data/airports.ts +1 -1
  2. package/data/cities.ts +1 -1
  3. package/data/countries-110m.ts +1 -1
  4. package/data/countries-dots-3.ts +1 -1
  5. package/data/countries-dots-4.ts +1 -1
  6. package/dist/lib/browser/chunk-GMWLKTLN.mjs +9 -0
  7. package/dist/lib/browser/{countries-110m-WI4PCLDF.mjs → countries-110m-ZM3ZIEFS.mjs} +2 -2
  8. package/dist/lib/browser/countries-110m-ZM3ZIEFS.mjs.map +7 -0
  9. package/dist/lib/browser/data.mjs +1 -1
  10. package/dist/lib/browser/index.mjs +187 -159
  11. package/dist/lib/browser/index.mjs.map +3 -3
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/node-esm/{chunk-PIIEDZEU.mjs → chunk-JODBF4CC.mjs} +3 -3
  14. package/dist/lib/node-esm/{countries-110m-DQ4XRC4B.mjs → countries-110m-3SFASWVD.mjs} +2 -2
  15. package/dist/lib/node-esm/countries-110m-3SFASWVD.mjs.map +7 -0
  16. package/dist/lib/node-esm/data.mjs +1 -1
  17. package/dist/lib/node-esm/index.mjs +187 -159
  18. package/dist/lib/node-esm/index.mjs.map +3 -3
  19. package/dist/lib/node-esm/meta.json +1 -1
  20. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  21. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  22. package/dist/types/src/components/Globe/Globe.stories.d.ts +1 -1
  23. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/Map/Map.d.ts +25 -12
  25. package/dist/types/src/components/Map/Map.d.ts.map +1 -1
  26. package/dist/types/src/components/Map/Map.stories.d.ts +3 -3
  27. package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
  29. package/dist/types/src/components/index.d.ts +0 -1
  30. package/dist/types/src/components/index.d.ts.map +1 -1
  31. package/dist/types/src/hooks/context.d.ts +7 -7
  32. package/dist/types/src/hooks/context.d.ts.map +1 -1
  33. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +1 -1
  34. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -1
  35. package/dist/types/src/hooks/useMapZoomHandler.d.ts +1 -1
  36. package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -1
  37. package/dist/types/src/hooks/useSpinner.d.ts +1 -1
  38. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -1
  39. package/dist/types/src/hooks/useTour.d.ts +4 -3
  40. package/dist/types/src/hooks/useTour.d.ts.map +1 -1
  41. package/dist/types/src/index.d.ts +1 -1
  42. package/dist/types/src/index.d.ts.map +1 -1
  43. package/dist/types/src/types.d.ts +2 -1
  44. package/dist/types/src/types.d.ts.map +1 -1
  45. package/dist/types/src/util/path.d.ts +5 -8
  46. package/dist/types/src/util/path.d.ts.map +1 -1
  47. package/dist/types/src/util/render.d.ts +4 -4
  48. package/dist/types/src/util/render.d.ts.map +1 -1
  49. package/dist/types/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +14 -11
  51. package/src/components/Globe/Globe.stories.tsx +29 -24
  52. package/src/components/Globe/Globe.tsx +74 -58
  53. package/src/components/Map/Map.stories.tsx +16 -7
  54. package/src/components/Map/Map.tsx +206 -91
  55. package/src/components/Toolbar/Controls.tsx +2 -6
  56. package/src/components/index.ts +0 -2
  57. package/src/hooks/context.tsx +10 -10
  58. package/src/hooks/useGlobeZoomHandler.ts +3 -3
  59. package/src/hooks/useMapZoomHandler.ts +1 -1
  60. package/src/hooks/useSpinner.ts +2 -1
  61. package/src/hooks/useTour.ts +9 -8
  62. package/src/index.ts +1 -1
  63. package/src/types.ts +3 -1
  64. package/src/util/inertia.ts +1 -1
  65. package/src/util/path.ts +5 -6
  66. package/src/util/render.ts +5 -3
  67. package/dist/lib/browser/chunk-ENCWOTYX.mjs +0 -9
  68. package/dist/lib/browser/countries-110m-WI4PCLDF.mjs.map +0 -7
  69. package/dist/lib/node/chunk-LAICG6L2.cjs +0 -40
  70. package/dist/lib/node/chunk-LAICG6L2.cjs.map +0 -7
  71. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs +0 -37877
  72. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs.map +0 -7
  73. package/dist/lib/node/data.cjs +0 -28
  74. package/dist/lib/node/data.cjs.map +0 -7
  75. package/dist/lib/node/index.cjs +0 -1187
  76. package/dist/lib/node/index.cjs.map +0 -7
  77. package/dist/lib/node/meta.json +0 -1
  78. package/dist/lib/node-esm/countries-110m-DQ4XRC4B.mjs.map +0 -7
  79. package/dist/types/src/components/types.d.ts +0 -15
  80. package/dist/types/src/components/types.d.ts.map +0 -1
  81. package/src/components/types.ts +0 -19
  82. /package/dist/lib/browser/{chunk-ENCWOTYX.mjs.map → chunk-GMWLKTLN.mjs.map} +0 -0
  83. /package/dist/lib/node-esm/{chunk-PIIEDZEU.mjs.map → chunk-JODBF4CC.mjs.map} +0 -0
@@ -2,109 +2,223 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- // eslint-disable-next-line no-restricted-imports
6
5
  import 'leaflet/dist/leaflet.css';
7
6
 
8
- import L, { Control, DomEvent, DomUtil, latLngBounds, type ControlPosition, type LatLngExpression } from 'leaflet';
9
- import React, { forwardRef, useEffect, useImperativeHandle, type PropsWithChildren } from 'react';
7
+ import { createContext } from '@radix-ui/react-context';
8
+ import L, { Control, type ControlPosition, DomEvent, DomUtil, type LatLngLiteral, latLngBounds } from 'leaflet';
9
+ import React, { type PropsWithChildren, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
10
10
  import { createRoot } from 'react-dom/client';
11
11
  import type { MapContainerProps } from 'react-leaflet';
12
12
  import { MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet';
13
- import { useResizeDetector } from 'react-resize-detector';
14
13
 
15
14
  import { debounce } from '@dxos/async';
16
- import { ThemeProvider, Tooltip, type ThemedClassName } from '@dxos/react-ui';
15
+ import { ThemeProvider, type ThemedClassName, Tooltip } from '@dxos/react-ui';
17
16
  import { defaultTx, mx } from '@dxos/react-ui-theme';
18
17
 
19
- import { ActionControls, controlPositions, ZoomControls, type ControlProps } from '../Toolbar';
20
- import { type MapCanvasProps } from '../types';
18
+ import { type GeoMarker } from '../../types';
19
+ import { ActionControls, type ControlProps, ZoomControls, controlPositions } from '../Toolbar';
21
20
 
22
21
  // TODO(burdon): Explore plugins: https://www.npmjs.com/search?q=keywords%3Areact-leaflet-v4
23
22
  // TODO(burdon): react-leaflet v5 is not compatible with react 18.
23
+ // TODO(burdon): Guess initial location.
24
24
 
25
25
  const defaults = {
26
- // TODO(burdon): Guess location.
27
- center: { lat: 51, lng: 0 } as L.LatLngExpression,
26
+ center: { lat: 51, lng: 0 } as L.LatLngLiteral,
28
27
  zoom: 4,
29
28
  };
30
29
 
31
30
  //
32
- // Root
31
+ // Controller
33
32
  //
34
33
 
35
- type MapRootProps = ThemedClassName<MapContainerProps>;
36
-
37
- // https://react-leaflet.js.org/docs/api-map
38
- const MapRoot = ({ classNames, center = defaults.center, zoom = defaults.zoom, ...props }: MapRootProps) => {
39
- return (
40
- <MapContainer
41
- className={mx('relative grid grow bg-baseSurface', classNames)}
42
- attributionControl={false}
43
- // TODO(burdon): Only if attention.
44
- scrollWheelZoom={true}
45
- zoomControl={false}
46
- center={center}
47
- zoom={zoom}
48
- {...props}
49
- />
50
- );
34
+ type MapController = {
35
+ setCenter: (center: LatLngLiteral, zoom?: number) => void;
36
+ setZoom: (cb: (zoom: number) => number) => void;
51
37
  };
52
38
 
53
39
  //
54
- // Control
40
+ // Context
55
41
  //
56
42
 
57
- // TODO(burdon): Normalize with Globe.
58
- type MapController = {
59
- setCenter: (center: LatLngExpression, zoom?: number) => void;
60
- setZoom: (cb: (zoom: number) => number) => void;
43
+ type MapContextValue = {
44
+ attention?: boolean;
61
45
  };
62
46
 
63
- const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center, zoom, onChange }, forwardedRef) => {
64
- const { ref, width, height } = useResizeDetector({ refreshRate: 200 });
65
- const map = useMap();
47
+ const [MapContextProvier, useMapContext] = createContext<MapContextValue>('Map');
48
+
49
+ //
50
+ // Root
51
+ //
52
+
53
+ type MapRootProps = ThemedClassName<
54
+ MapContainerProps & {
55
+ onChange?: (ev: { center: LatLngLiteral; zoom: number }) => void;
56
+ }
57
+ >;
66
58
 
67
- useImperativeHandle(
59
+ /**
60
+ * https://react-leaflet.js.org/docs/api-map
61
+ */
62
+ const MapRoot = forwardRef<MapController, MapRootProps>(
63
+ (
64
+ {
65
+ classNames,
66
+ scrollWheelZoom = true,
67
+ doubleClickZoom = true,
68
+ touchZoom = true,
69
+ center = defaults.center,
70
+ zoom = defaults.zoom,
71
+ onChange,
72
+ ...props
73
+ },
68
74
  forwardedRef,
69
- () => ({
70
- setCenter: (center: LatLngExpression, zoom?: number) => {
71
- map.setView(center, zoom);
72
- },
73
- setZoom: (cb) => {
74
- map.setZoom(cb(map.getZoom()));
75
- },
76
- }),
77
- [map],
78
- );
75
+ ) => {
76
+ const [attention, setAttention] = useState(false);
77
+ const mapRef = useRef<L.Map>(null);
78
+ const map = mapRef.current;
79
79
 
80
- // Resize.
81
- useEffect(() => {
82
- if (width && height) {
83
- map.invalidateSize();
84
- }
85
- }, [width, height]);
80
+ useImperativeHandle(
81
+ forwardedRef,
82
+ () => ({
83
+ setCenter: (center: LatLngLiteral, zoom?: number) => {
84
+ mapRef.current?.setView(center, zoom);
85
+ },
86
+ setZoom: (cb: (zoom: number) => number) => {
87
+ mapRef.current?.setZoom(cb(mapRef.current?.getZoom() ?? 0));
88
+ },
89
+ }),
90
+ [],
91
+ );
92
+
93
+ // Events.
94
+ useEffect(() => {
95
+ if (!map) {
96
+ return;
97
+ }
98
+
99
+ const handler = debounce(() => {
100
+ setAttention(true);
101
+ onChange?.({
102
+ center: map.getCenter(),
103
+ zoom: map.getZoom(),
104
+ });
105
+ }, 100);
106
+
107
+ map.on('move', handler);
108
+ map.on('zoom', handler);
109
+ map.on('focus', () => setAttention(true));
110
+ map.on('blur', () => setAttention(false));
111
+ return () => {
112
+ map.off('move');
113
+ map.off('zoom');
114
+ map.off('focus');
115
+ map.off('blur');
116
+ };
117
+ }, [map, onChange]);
118
+
119
+ // Enable/disable scroll wheel zoom.
120
+ // TODO(burdon): Use attention:
121
+ // const {hasAttention} = useAttention(props.id);
122
+ useEffect(() => {
123
+ if (!map) {
124
+ return;
125
+ }
126
+
127
+ if (attention) {
128
+ map.scrollWheelZoom.enable();
129
+ } else {
130
+ map.scrollWheelZoom.disable();
131
+ }
132
+ }, [map, attention]);
133
+
134
+ return (
135
+ <MapContextProvier attention={attention}>
136
+ <MapContainer
137
+ {...props}
138
+ ref={mapRef}
139
+ className={mx('group relative grid bs-full is-full !bg-baseSurface dx-focus-ring-inset', classNames)}
140
+ attributionControl={false}
141
+ zoomControl={false}
142
+ scrollWheelZoom={scrollWheelZoom}
143
+ doubleClickZoom={doubleClickZoom}
144
+ touchZoom={touchZoom}
145
+ center={center}
146
+ zoom={zoom}
147
+ // whenReady={() => {}}
148
+ />
149
+ </MapContextProvier>
150
+ );
151
+ },
152
+ );
153
+
154
+ MapRoot.displayName = 'Map.Root';
155
+
156
+ //
157
+ // Tiles
158
+ // https://react-leaflet.js.org/docs/api-components/#tilelayer
159
+ //
160
+
161
+ type MapTilesProps = {};
86
162
 
87
- // Position.
163
+ const MapTiles = (_props: MapTilesProps) => {
164
+ const ref = useRef<L.TileLayer>(null);
165
+
166
+ // NOTE: Need to dynamically update data attribute since TileLayer doesn't update, but
167
+ // Tailwind requires setting the property for static analysis.
168
+ const { attention } = useMapContext(MapTiles.displayName);
88
169
  useEffect(() => {
89
- if (center) {
90
- map.setView(center, zoom);
91
- } else if (zoom !== undefined) {
92
- map.setZoom(zoom);
170
+ if (ref.current) {
171
+ ref.current.getContainer().dataset.attention = attention ? '1' : '0';
93
172
  }
94
- }, [center, zoom]);
173
+ }, [attention]);
95
174
 
96
- // Events.
97
- useEffect(() => {
98
- const handler = debounce(() => {
99
- onChange?.({ center: map.getCenter(), zoom: map.getZoom() });
100
- }, 100);
101
- map.on('move', handler);
102
- map.on('zoom', handler);
103
- return () => {
104
- map.off('move', handler);
105
- map.off('zoom', handler);
106
- };
107
- }, [map, onChange]);
175
+ // TODO(burdon): Option to add class 'invert'.
176
+ return (
177
+ <>
178
+ <TileLayer
179
+ ref={ref}
180
+ data-attention={attention}
181
+ detectRetina={true}
182
+ className='dark:grayscale dark:invert data-[attention="0"]:!opacity-80'
183
+ url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
184
+ keepBuffer={4}
185
+ // opacity={attention ? 1 : 0.7}
186
+ />
187
+
188
+ {/* Temperature map. */}
189
+ {/* <WMSTileLayer
190
+ url='https://gibs.earthdata.nasa.gov/wms/epsg4326/best/wms.cgi'
191
+ layers='MODIS_Terra_Land_Surface_Temp_Day'
192
+ format='image/png'
193
+ transparent={true}
194
+ version='1.3.0'
195
+ attribution='NASA GIBS'
196
+ /> */}
197
+
198
+ {/* US Weather. */}
199
+ {/* <WMSTileLayer
200
+ url='https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi'
201
+ layers='nexrad-n0r' // layers='nexrad-n0r'
202
+ format='image/png'
203
+ transparent={true}
204
+ /> */}
205
+ </>
206
+ );
207
+ };
208
+
209
+ MapTiles.displayName = 'Map.Tiles';
210
+
211
+ //
212
+ // Markers
213
+ //
214
+
215
+ type MapMarkersProps = {
216
+ markers?: GeoMarker[];
217
+ selected?: string[];
218
+ };
219
+
220
+ const MapMarkers = ({ selected, markers }: MapMarkersProps) => {
221
+ const map = useMap();
108
222
 
109
223
  // Set the viewport around the markers, or show the whole world map if `markers` is empty.
110
224
  useEffect(() => {
@@ -117,14 +231,7 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
117
231
  }, [markers]);
118
232
 
119
233
  return (
120
- <div ref={ref} className='flex w-full h-full overflow-hidden bg-baseSurface'>
121
- {/* Map tiles. */}
122
- <TileLayer
123
- className='dark:filter dark:grayscale dark:invert'
124
- url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
125
- />
126
-
127
- {/* Markers. */}
234
+ <>
128
235
  {markers?.map(({ id, title, location: { lat, lng } }) => {
129
236
  return (
130
237
  <Marker
@@ -132,6 +239,7 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
132
239
  position={{ lat, lng }}
133
240
  icon={
134
241
  // TODO(burdon): Create custom icon from bundled assets.
242
+ // TODO(burdon): Selection state.
135
243
  new L.Icon({
136
244
  iconUrl: 'https://dxos.network/marker-icon.png',
137
245
  iconRetinaUrl: 'https://dxos.network/marker-icon-2x.png',
@@ -147,9 +255,11 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
147
255
  </Marker>
148
256
  );
149
257
  })}
150
- </div>
258
+ </>
151
259
  );
152
- });
260
+ };
261
+
262
+ MapMarkers.displayName = 'Map.Markers';
153
263
 
154
264
  //
155
265
  // Controls
@@ -192,23 +302,28 @@ const CustomControl = ({
192
302
 
193
303
  type MapControlProps = { position?: ControlPosition } & Pick<ControlProps, 'onAction'>;
194
304
 
305
+ const MapZoom = ({ onAction, position = 'bottomleft', ...props }: MapControlProps) => (
306
+ <CustomControl position={position} {...props}>
307
+ <ZoomControls onAction={onAction} />
308
+ </CustomControl>
309
+ );
310
+
311
+ const MapAction = ({ onAction, position = 'bottomright', ...props }: MapControlProps) => (
312
+ <CustomControl position={position} {...props}>
313
+ <ActionControls onAction={onAction} />
314
+ </CustomControl>
315
+ );
316
+
195
317
  //
196
318
  // Map
197
319
  //
198
320
 
199
321
  export const Map = {
200
322
  Root: MapRoot,
201
- Canvas: MapCanvas,
202
- Zoom: ({ onAction, position = 'bottomleft', ...props }: MapControlProps) => (
203
- <CustomControl position={position} {...props}>
204
- <ZoomControls onAction={onAction} />
205
- </CustomControl>
206
- ),
207
- Action: ({ onAction, position = 'bottomright', ...props }: MapControlProps) => (
208
- <CustomControl position={position} {...props}>
209
- <ActionControls onAction={onAction} />
210
- </CustomControl>
211
- ),
323
+ Tiles: MapTiles,
324
+ Markers: MapMarkers,
325
+ Zoom: MapZoom,
326
+ Action: MapAction,
212
327
  };
213
328
 
214
- export { type MapCanvasProps, type MapController };
329
+ export { type MapController, type MapRootProps, type MapTilesProps, type MapMarkersProps, type MapControlProps };
@@ -22,9 +22,8 @@ export const controlPositions: Record<ControlPosition, string> = {
22
22
 
23
23
  export const ZoomControls = ({ classNames, onAction }: ControlProps) => {
24
24
  return (
25
- <Toolbar.Root classNames={['gap-1', classNames]}>
25
+ <Toolbar.Root classNames={['gap-2', classNames]}>
26
26
  <IconButton
27
- //
28
27
  icon='ph--plus--regular'
29
28
  label='zoom in'
30
29
  iconOnly
@@ -33,7 +32,6 @@ export const ZoomControls = ({ classNames, onAction }: ControlProps) => {
33
32
  onClick={() => onAction?.('zoom-in')}
34
33
  />
35
34
  <IconButton
36
- //
37
35
  icon='ph--minus--regular'
38
36
  label='zoom out'
39
37
  iconOnly
@@ -47,9 +45,8 @@ export const ZoomControls = ({ classNames, onAction }: ControlProps) => {
47
45
 
48
46
  export const ActionControls = ({ classNames, onAction }: ControlProps) => {
49
47
  return (
50
- <Toolbar.Root classNames={['gap-1', classNames]}>
48
+ <Toolbar.Root classNames={['gap-2', classNames]}>
51
49
  <IconButton
52
- //
53
50
  icon='ph--play--regular'
54
51
  label='start'
55
52
  iconOnly
@@ -58,7 +55,6 @@ export const ActionControls = ({ classNames, onAction }: ControlProps) => {
58
55
  onClick={() => onAction?.('start')}
59
56
  />
60
57
  <IconButton
61
- //
62
58
  icon='ph--globe-hemisphere-west--regular'
63
59
  label='toggle'
64
60
  iconOnly
@@ -2,8 +2,6 @@
2
2
  // Copyright 2019 DXOS.org
3
3
  //
4
4
 
5
- export * from './types';
6
-
7
5
  export * from './Globe';
8
6
  export * from './Map';
9
7
  export * from './Toolbar';
@@ -2,12 +2,12 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { createContext, type Dispatch, type PropsWithChildren, type SetStateAction, useContext } from 'react';
5
+ import React, { type Dispatch, type PropsWithChildren, type SetStateAction, createContext, useContext } from 'react';
6
6
 
7
7
  import { raise } from '@dxos/debug';
8
8
  import { useControlledState } from '@dxos/react-ui';
9
9
 
10
- import { type LatLng } from '../util';
10
+ import { type LatLngLiteral } from '../types';
11
11
 
12
12
  // TODO(burdon): Factor out common geometry types.
13
13
  export type Size = { width: number; height: number };
@@ -16,12 +16,12 @@ export type Vector = [number, number, number];
16
16
 
17
17
  export type GlobeContextType = {
18
18
  size: Size;
19
- center: LatLng;
20
- scale: number;
19
+ center?: LatLngLiteral;
20
+ zoom: number;
21
21
  translation: Point;
22
22
  rotation: Vector;
23
- setCenter: Dispatch<SetStateAction<LatLng>>;
24
- setScale: Dispatch<SetStateAction<number>>;
23
+ setCenter: Dispatch<SetStateAction<LatLngLiteral>>;
24
+ setZoom: Dispatch<SetStateAction<number>>;
25
25
  setTranslation: Dispatch<SetStateAction<Point>>;
26
26
  setRotation: Dispatch<SetStateAction<Vector>>;
27
27
  };
@@ -29,25 +29,25 @@ export type GlobeContextType = {
29
29
  const GlobeContext = createContext<GlobeContextType>(undefined);
30
30
 
31
31
  export type GlobeContextProviderProps = PropsWithChildren<
32
- Partial<Pick<GlobeContextType, 'size' | 'center' | 'scale' | 'translation' | 'rotation'>>
32
+ Partial<Pick<GlobeContextType, 'size' | 'center' | 'zoom' | 'translation' | 'rotation'>>
33
33
  >;
34
34
 
35
35
  export const GlobeContextProvider = ({
36
36
  children,
37
37
  size,
38
38
  center: _center,
39
- scale: _scale,
39
+ zoom: _zoom,
40
40
  translation: _translation,
41
41
  rotation: _rotation,
42
42
  }: GlobeContextProviderProps) => {
43
43
  const [center, setCenter] = useControlledState(_center);
44
- const [scale, setScale] = useControlledState(_scale);
44
+ const [zoom, setZoom] = useControlledState(_zoom);
45
45
  const [translation, setTranslation] = useControlledState<Point>(_translation);
46
46
  const [rotation, setRotation] = useControlledState<Vector>(_rotation);
47
47
 
48
48
  return (
49
49
  <GlobeContext.Provider
50
- value={{ size, center, scale, translation, rotation, setCenter, setScale, setTranslation, setRotation }}
50
+ value={{ size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation }}
51
51
  >
52
52
  {children}
53
53
  </GlobeContext.Provider>
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useCallback } from 'react';
6
6
 
7
- import { type GlobeController, type ControlProps } from '../components';
7
+ import { type ControlProps, type GlobeController } from '../components';
8
8
 
9
9
  export const useGlobeZoomHandler = (controller: GlobeController | null | undefined): ControlProps['onAction'] => {
10
10
  return useCallback<ControlProps['onAction']>(
@@ -15,11 +15,11 @@ export const useGlobeZoomHandler = (controller: GlobeController | null | undefin
15
15
 
16
16
  switch (event) {
17
17
  case 'zoom-in': {
18
- controller.setScale((scale) => scale * 1.1);
18
+ controller.setZoom((zoom) => zoom * 1.1);
19
19
  break;
20
20
  }
21
21
  case 'zoom-out': {
22
- controller.setScale((scale) => scale * 0.9);
22
+ controller.setZoom((zoom) => zoom * 0.9);
23
23
  break;
24
24
  }
25
25
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useCallback } from 'react';
6
6
 
7
- import { type MapController, type ControlProps } from '../components';
7
+ import { type ControlProps, type MapController } from '../components';
8
8
 
9
9
  export const useMapZoomHandler = (controller: MapController | null | undefined): ControlProps['onAction'] => {
10
10
  return useCallback<ControlProps['onAction']>(
@@ -6,9 +6,10 @@ import { timer as d3Timer } from 'd3';
6
6
  import { type Timer } from 'd3';
7
7
  import { useEffect, useState } from 'react';
8
8
 
9
- import { type Vector } from './context';
10
9
  import { type GlobeController } from '../components';
11
10
 
11
+ import { type Vector } from './context';
12
+
12
13
  export type SpinnerOptions = {
13
14
  disabled?: boolean;
14
15
  delta?: Vector;
@@ -2,12 +2,13 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { geoPath, geoInterpolate, geoDistance, selection as d3Selection } from 'd3';
6
- import { type SetStateAction, type Dispatch, useEffect, useState, useMemo } from 'react';
5
+ import { selection as d3Selection, geoDistance, geoInterpolate, geoPath } from 'd3';
6
+ import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react';
7
7
  import versor from 'versor';
8
8
 
9
9
  import type { GlobeController } from '../components';
10
- import { geoToPosition, type LatLng, positionToRotation, type StyleSet } from '../util';
10
+ import { type LatLngLiteral } from '../types';
11
+ import { type StyleSet, geoToPosition, positionToRotation } from '../util';
11
12
 
12
13
  const TRANSITION_NAME = 'globe-tour';
13
14
 
@@ -29,7 +30,7 @@ export type TourOptions = {
29
30
  */
30
31
  export const useTour = (
31
32
  controller?: GlobeController | null,
32
- points?: LatLng[],
33
+ points?: LatLngLiteral[],
33
34
  options: TourOptions = {},
34
35
  ): [boolean, Dispatch<SetStateAction<boolean>>] => {
35
36
  const selection = useMemo(() => d3Selection(), []);
@@ -48,7 +49,7 @@ export const useTour = (
48
49
  const path = geoPath(projection, context).pointRadius(2);
49
50
 
50
51
  const tilt = options.tilt ?? 0;
51
- let last: LatLng;
52
+ let last: LatLngLiteral;
52
53
  try {
53
54
  const p = [...points];
54
55
  if (options.loop) {
@@ -82,14 +83,14 @@ export const useTour = (
82
83
  {
83
84
  context.beginPath();
84
85
  context.strokeStyle = options?.styles?.arc?.strokeStyle ?? 'yellow';
85
- context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.scale ?? 1);
86
+ context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.zoom ?? 1);
86
87
  context.setLineDash(options?.styles?.arc?.lineDash ?? []);
87
88
  path({ type: 'LineString', coordinates: [ip(t1), ip(t2)] });
88
89
  context.stroke();
89
90
 
90
91
  context.beginPath();
91
92
  context.fillStyle = options?.styles?.cursor?.fillStyle ?? 'orange';
92
- path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.scale ?? 1));
93
+ path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.zoom ?? 1));
93
94
  path({ type: 'Point', coordinates: ip(t2) });
94
95
  context.fill();
95
96
  }
@@ -104,7 +105,7 @@ export const useTour = (
104
105
  await transition.end();
105
106
  last = next;
106
107
  }
107
- } catch (err) {
108
+ } catch {
108
109
  // Ignore.
109
110
  } finally {
110
111
  setRunning(false);
package/src/index.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  export * from './components';
6
6
  export * from './data';
7
7
  export * from './hooks';
8
- export * from './types';
8
+ export type * from './types';
9
9
  export * from './util';
package/src/types.ts CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  import { type LatLngLiteral } from 'leaflet';
6
6
 
7
- export type MapMarker = {
7
+ export { type LatLngLiteral };
8
+
9
+ export type GeoMarker = {
8
10
  id: string;
9
11
  title?: string;
10
12
  location: LatLngLiteral;
@@ -4,7 +4,7 @@
4
4
  // https://github.com/Fil/d3-inertia
5
5
  //
6
6
 
7
- import { select, drag, timer } from 'd3';
7
+ import { drag, select, timer } from 'd3';
8
8
  import versor from 'versor';
9
9
 
10
10
  export const restrictAxis =
package/src/util/path.ts CHANGED
@@ -4,22 +4,21 @@
4
4
 
5
5
  import { type GeoGeometryObjects, geoCircle as d3GeoCircle } from 'd3';
6
6
  import { type Point, type Polygon, type Position } from 'geojson';
7
+ import { type LatLngLiteral } from 'leaflet';
7
8
 
8
9
  import type { Vector } from '../hooks';
9
10
 
10
- export type LatLng = { lat: number; lng: number };
11
-
12
11
  export const positionToRotation = ([lng, lat]: [number, number], tilt = 0): Vector => [-lng, tilt - lat, 0];
13
12
 
14
- export const geoToPosition = ({ lat, lng }: LatLng): [number, number] => [lng, lat];
13
+ export const geoToPosition = ({ lat, lng }: LatLngLiteral): [number, number] => [lng, lat];
15
14
 
16
- export const geoPoint = (point: LatLng): Point => ({ type: 'Point', coordinates: geoToPosition(point) });
15
+ export const geoPoint = (point: LatLngLiteral): Point => ({ type: 'Point', coordinates: geoToPosition(point) });
17
16
 
18
17
  // https://github.com/d3/d3-geo#geoCircle
19
- export const geoCircle = ({ lat, lng }: LatLng, radius: number): Polygon =>
18
+ export const geoCircle = ({ lat, lng }: LatLngLiteral, radius: number): Polygon =>
20
19
  d3GeoCircle().radius(radius).center([lng, lat])();
21
20
 
22
- export const geoLine = (p1: LatLng, p2: LatLng): GeoGeometryObjects => ({
21
+ export const geoLine = (p1: LatLngLiteral, p2: LatLngLiteral): GeoGeometryObjects => ({
23
22
  type: 'LineString',
24
23
  coordinates: [
25
24
  [p1.lng, p1.lat],
@@ -6,7 +6,9 @@ import { type GeoPath, type GeoPermissibleObjects, geoGraticule } from 'd3';
6
6
  import { feature, mesh } from 'topojson-client';
7
7
  import { type Topology } from 'topojson-specification';
8
8
 
9
- import { type LatLng, geoLine, geoPoint } from './path';
9
+ import { type LatLngLiteral } from '../types';
10
+
11
+ import { geoLine, geoPoint } from './path';
10
12
 
11
13
  export type Styles = Record<string, any>;
12
14
 
@@ -25,8 +27,8 @@ export type Style =
25
27
  export type StyleSet = Partial<Record<Style, Styles>>;
26
28
 
27
29
  export type Features = {
28
- points?: LatLng[];
29
- lines?: { source: LatLng; target: LatLng }[];
30
+ points?: LatLngLiteral[];
31
+ lines?: { source: LatLngLiteral; target: LatLngLiteral }[];
30
32
  };
31
33
 
32
34
  export type Layer = {