@dxos/react-ui-geo 0.8.4-main.84f28bd → 0.8.4-main.ae835ea

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 (74) 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-37VAAFCK.mjs → countries-110m-ZM3ZIEFS.mjs} +1 -1
  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 -163
  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-OPJPAAEK.mjs → chunk-JODBF4CC.mjs} +2 -2
  14. package/dist/lib/node-esm/{countries-110m-36TTKK5B.mjs → countries-110m-3SFASWVD.mjs} +1 -1
  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 -163
  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 +25 -9
  23. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/Map/Map.d.ts +27 -17
  25. package/dist/types/src/components/Map/Map.d.ts.map +1 -1
  26. package/dist/types/src/components/Map/Map.stories.d.ts +14 -8
  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 +22 -19
  51. package/src/components/Globe/Globe.stories.tsx +81 -33
  52. package/src/components/Globe/Globe.tsx +78 -61
  53. package/src/components/Map/Map.stories.tsx +25 -14
  54. package/src/components/Map/Map.tsx +180 -95
  55. package/src/components/Toolbar/Controls.tsx +2 -6
  56. package/src/components/index.ts +0 -2
  57. package/src/hooks/context.tsx +22 -16
  58. package/src/hooks/useGlobeZoomHandler.ts +9 -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-CYCBMCOP.mjs +0 -9
  68. package/dist/lib/browser/countries-110m-37VAAFCK.mjs.map +0 -7
  69. package/dist/lib/node-esm/countries-110m-36TTKK5B.mjs.map +0 -7
  70. package/dist/types/src/components/types.d.ts +0 -15
  71. package/dist/types/src/components/types.d.ts.map +0 -1
  72. package/src/components/types.ts +0 -19
  73. /package/dist/lib/browser/{chunk-CYCBMCOP.mjs.map → chunk-GMWLKTLN.mjs.map} +0 -0
  74. /package/dist/lib/node-esm/{chunk-OPJPAAEK.mjs.map → chunk-JODBF4CC.mjs.map} +0 -0
@@ -2,109 +2,193 @@
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
- import type { MapContainerProps } from 'react-leaflet';
12
- import { MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet';
13
- import { useResizeDetector } from 'react-resize-detector';
11
+ import { MapContainer, type MapContainerProps, Marker, Popup, TileLayer, useMap, useMapEvents } from 'react-leaflet';
14
12
 
15
- import { debounce } from '@dxos/async';
16
- import { ThemeProvider, Tooltip, type ThemedClassName } from '@dxos/react-ui';
13
+ import { ThemeProvider, type ThemedClassName, Tooltip } from '@dxos/react-ui';
17
14
  import { defaultTx, mx } from '@dxos/react-ui-theme';
18
15
 
19
- import { ActionControls, controlPositions, ZoomControls, type ControlProps } from '../Toolbar';
20
- import { type MapCanvasProps } from '../types';
16
+ import { type GeoMarker } from '../../types';
17
+ import { ActionControls, type ControlProps, ZoomControls, controlPositions } from '../Toolbar';
21
18
 
22
19
  // TODO(burdon): Explore plugins: https://www.npmjs.com/search?q=keywords%3Areact-leaflet-v4
23
20
  // TODO(burdon): react-leaflet v5 is not compatible with react 18.
21
+ // TODO(burdon): Guess initial location.
24
22
 
25
23
  const defaults = {
26
- // TODO(burdon): Guess location.
27
- center: { lat: 51, lng: 0 } as L.LatLngExpression,
24
+ center: { lat: 51, lng: 0 } as L.LatLngLiteral,
28
25
  zoom: 4,
29
- };
26
+ } as const;
30
27
 
31
28
  //
32
- // Root
29
+ // Controller
33
30
  //
34
31
 
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
- );
32
+ type MapController = {
33
+ setCenter: (center: LatLngLiteral, zoom?: number) => void;
34
+ setZoom: (cb: (zoom: number) => number) => void;
51
35
  };
52
36
 
53
37
  //
54
- // Control
38
+ // Context
55
39
  //
56
40
 
57
- // TODO(burdon): Normalize with Globe.
58
- type MapController = {
59
- setCenter: (center: LatLngExpression, zoom?: number) => void;
60
- setZoom: (cb: (zoom: number) => number) => void;
41
+ type MapContextValue = {
42
+ attention?: boolean;
43
+ onChange?: (ev: { center: LatLngLiteral; zoom: number }) => void;
61
44
  };
62
45
 
63
- const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center, zoom, onChange }, forwardedRef) => {
64
- const { ref, width, height } = useResizeDetector({ refreshRate: 200 });
65
- const map = useMap();
46
+ const [MapContextProvier, useMapContext] = createContext<MapContextValue>('Map');
47
+
48
+ //
49
+ // Root
50
+ //
66
51
 
67
- useImperativeHandle(
52
+ type MapRootProps = ThemedClassName<MapContainerProps & Pick<MapContextValue, 'onChange'>>;
53
+
54
+ /**
55
+ * https://react-leaflet.js.org/docs/api-map
56
+ */
57
+ const MapRoot = forwardRef<MapController, MapRootProps>(
58
+ (
59
+ { classNames, scrollWheelZoom = true, doubleClickZoom = true, touchZoom = true, center, zoom, onChange, ...props },
68
60
  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
- );
61
+ ) => {
62
+ const [attention, setAttention] = useState(false);
63
+ const mapRef = useRef<L.Map>(null);
64
+ const map = mapRef.current;
79
65
 
80
- // Resize.
81
- useEffect(() => {
82
- if (width && height) {
83
- map.invalidateSize();
84
- }
85
- }, [width, height]);
66
+ useImperativeHandle(
67
+ forwardedRef,
68
+ () => ({
69
+ setCenter: (center: LatLngLiteral, zoom?: number) => {
70
+ mapRef.current?.setView(center, zoom);
71
+ },
72
+ setZoom: (cb: (zoom: number) => number) => {
73
+ mapRef.current?.setZoom(cb(mapRef.current?.getZoom() ?? 0));
74
+ },
75
+ }),
76
+ [],
77
+ );
78
+
79
+ // Enable/disable scroll wheel zoom.
80
+ // TODO(burdon): Use attention:
81
+ // const {hasAttention} = useAttention(props.id);
82
+ useEffect(() => {
83
+ if (!map) {
84
+ return;
85
+ }
86
+
87
+ if (attention) {
88
+ map.scrollWheelZoom.enable();
89
+ } else {
90
+ map.scrollWheelZoom.disable();
91
+ }
92
+ }, [map, attention]);
93
+
94
+ return (
95
+ <MapContextProvier attention={attention} onChange={onChange}>
96
+ <MapContainer
97
+ {...props}
98
+ ref={mapRef}
99
+ className={mx('group relative grid bs-full is-full !bg-baseSurface dx-focus-ring-inset', classNames)}
100
+ attributionControl={false}
101
+ zoomControl={false}
102
+ scrollWheelZoom={scrollWheelZoom}
103
+ doubleClickZoom={doubleClickZoom}
104
+ touchZoom={touchZoom}
105
+ center={center ?? defaults.center}
106
+ zoom={zoom ?? defaults.zoom}
107
+ // whenReady={() => {}}
108
+ />
109
+ </MapContextProvier>
110
+ );
111
+ },
112
+ );
113
+
114
+ MapRoot.displayName = 'Map.Root';
115
+
116
+ //
117
+ // Tiles
118
+ // https://react-leaflet.js.org/docs/api-components/#tilelayer
119
+ //
120
+
121
+ type MapTilesProps = {};
122
+
123
+ const MapTiles = (_props: MapTilesProps) => {
124
+ const ref = useRef<L.TileLayer>(null);
125
+ const { onChange } = useMapContext(MapTiles.displayName);
126
+
127
+ useMapEvents({
128
+ zoomstart: (ev) => {
129
+ onChange?.({
130
+ center: ev.target.getCenter(),
131
+ zoom: ev.target.getZoom(),
132
+ });
133
+ },
134
+ });
86
135
 
87
- // Position.
136
+ // NOTE: Need to dynamically update data attribute since TileLayer doesn't update, but
137
+ // Tailwind requires setting the property for static analysis.
138
+ const { attention } = useMapContext(MapTiles.displayName);
88
139
  useEffect(() => {
89
- if (center) {
90
- map.setView(center, zoom);
91
- } else if (zoom !== undefined) {
92
- map.setZoom(zoom);
140
+ if (ref.current) {
141
+ ref.current.getContainer().dataset.attention = attention ? '1' : '0';
93
142
  }
94
- }, [center, zoom]);
143
+ }, [attention]);
95
144
 
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]);
145
+ // TODO(burdon): Option to add class 'invert'.
146
+ return (
147
+ <>
148
+ <TileLayer
149
+ ref={ref}
150
+ data-attention={attention}
151
+ detectRetina={true}
152
+ className='dark:grayscale dark:invert data-[attention="0"]:!opacity-80'
153
+ url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
154
+ keepBuffer={4}
155
+ // opacity={attention ? 1 : 0.7}
156
+ />
157
+
158
+ {/* Temperature map. */}
159
+ {/* <WMSTileLayer
160
+ url='https://gibs.earthdata.nasa.gov/wms/epsg4326/best/wms.cgi'
161
+ layers='MODIS_Terra_Land_Surface_Temp_Day'
162
+ format='image/png'
163
+ transparent={true}
164
+ version='1.3.0'
165
+ attribution='NASA GIBS'
166
+ /> */}
167
+
168
+ {/* US Weather. */}
169
+ {/* <WMSTileLayer
170
+ url='https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi'
171
+ layers='nexrad-n0r' // layers='nexrad-n0r'
172
+ format='image/png'
173
+ transparent={true}
174
+ /> */}
175
+ </>
176
+ );
177
+ };
178
+
179
+ MapTiles.displayName = 'Map.Tiles';
180
+
181
+ //
182
+ // Markers
183
+ //
184
+
185
+ type MapMarkersProps = {
186
+ markers?: GeoMarker[];
187
+ selected?: string[];
188
+ };
189
+
190
+ const MapMarkers = ({ selected, markers }: MapMarkersProps) => {
191
+ const map = useMap();
108
192
 
109
193
  // Set the viewport around the markers, or show the whole world map if `markers` is empty.
110
194
  useEffect(() => {
@@ -117,14 +201,7 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
117
201
  }, [markers]);
118
202
 
119
203
  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. */}
204
+ <>
128
205
  {markers?.map(({ id, title, location: { lat, lng } }) => {
129
206
  return (
130
207
  <Marker
@@ -132,6 +209,7 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
132
209
  position={{ lat, lng }}
133
210
  icon={
134
211
  // TODO(burdon): Create custom icon from bundled assets.
212
+ // TODO(burdon): Selection state.
135
213
  new L.Icon({
136
214
  iconUrl: 'https://dxos.network/marker-icon.png',
137
215
  iconRetinaUrl: 'https://dxos.network/marker-icon-2x.png',
@@ -147,9 +225,11 @@ const MapCanvas = forwardRef<MapController, MapCanvasProps>(({ markers, center,
147
225
  </Marker>
148
226
  );
149
227
  })}
150
- </div>
228
+ </>
151
229
  );
152
- });
230
+ };
231
+
232
+ MapMarkers.displayName = 'Map.Markers';
153
233
 
154
234
  //
155
235
  // Controls
@@ -192,23 +272,28 @@ const CustomControl = ({
192
272
 
193
273
  type MapControlProps = { position?: ControlPosition } & Pick<ControlProps, 'onAction'>;
194
274
 
275
+ const MapZoom = ({ onAction, position = 'bottomleft', ...props }: MapControlProps) => (
276
+ <CustomControl position={position} {...props}>
277
+ <ZoomControls onAction={onAction} />
278
+ </CustomControl>
279
+ );
280
+
281
+ const MapAction = ({ onAction, position = 'bottomright', ...props }: MapControlProps) => (
282
+ <CustomControl position={position} {...props}>
283
+ <ActionControls onAction={onAction} />
284
+ </CustomControl>
285
+ );
286
+
195
287
  //
196
288
  // Map
197
289
  //
198
290
 
199
291
  export const Map = {
200
292
  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
- ),
293
+ Tiles: MapTiles,
294
+ Markers: MapMarkers,
295
+ Zoom: MapZoom,
296
+ Action: MapAction,
212
297
  };
213
298
 
214
- export { type MapCanvasProps, type MapController };
299
+ 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,38 +16,44 @@ 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
  };
28
28
 
29
+ const defaults = {
30
+ center: { lat: 51, lng: 0 } as LatLngLiteral,
31
+ zoom: 4,
32
+ } as const;
33
+
34
+ // TODO(burdon): Replace with radix.
29
35
  const GlobeContext = createContext<GlobeContextType>(undefined);
30
36
 
31
37
  export type GlobeContextProviderProps = PropsWithChildren<
32
- Partial<Pick<GlobeContextType, 'size' | 'center' | 'scale' | 'translation' | 'rotation'>>
38
+ Partial<Pick<GlobeContextType, 'size' | 'center' | 'zoom' | 'translation' | 'rotation'>>
33
39
  >;
34
40
 
35
41
  export const GlobeContextProvider = ({
36
42
  children,
37
43
  size,
38
- center: _center,
39
- scale: _scale,
40
- translation: _translation,
41
- rotation: _rotation,
44
+ center: centerParam = defaults.center,
45
+ zoom: zoomParam = defaults.zoom,
46
+ translation: translationParam,
47
+ rotation: rotationParam,
42
48
  }: GlobeContextProviderProps) => {
43
- const [center, setCenter] = useControlledState(_center);
44
- const [scale, setScale] = useControlledState(_scale);
45
- const [translation, setTranslation] = useControlledState<Point>(_translation);
46
- const [rotation, setRotation] = useControlledState<Vector>(_rotation);
49
+ const [center, setCenter] = useControlledState(centerParam);
50
+ const [zoom, setZoom] = useControlledState(zoomParam);
51
+ const [translation, setTranslation] = useControlledState<Point>(translationParam);
52
+ const [rotation, setRotation] = useControlledState<Vector>(rotationParam);
47
53
 
48
54
  return (
49
55
  <GlobeContext.Provider
50
- value={{ size, center, scale, translation, rotation, setCenter, setScale, setTranslation, setRotation }}
56
+ value={{ size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation }}
51
57
  >
52
58
  {children}
53
59
  </GlobeContext.Provider>
@@ -4,7 +4,9 @@
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
+
9
+ const ZOOM_FACTOR = 0.1;
8
10
 
9
11
  export const useGlobeZoomHandler = (controller: GlobeController | null | undefined): ControlProps['onAction'] => {
10
12
  return useCallback<ControlProps['onAction']>(
@@ -15,11 +17,15 @@ export const useGlobeZoomHandler = (controller: GlobeController | null | undefin
15
17
 
16
18
  switch (event) {
17
19
  case 'zoom-in': {
18
- controller.setScale((scale) => scale * 1.1);
20
+ controller.setZoom((zoom) => {
21
+ return zoom * (1 + ZOOM_FACTOR);
22
+ });
19
23
  break;
20
24
  }
21
25
  case 'zoom-out': {
22
- controller.setScale((scale) => scale * 0.9);
26
+ controller.setZoom((zoom) => {
27
+ return zoom * (1 - ZOOM_FACTOR);
28
+ });
23
29
  break;
24
30
  }
25
31
  }
@@ -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 = {
@@ -1,9 +0,0 @@
1
- // src/data.ts
2
- var loadTopology = async () => {
3
- return (await import("./countries-110m-37VAAFCK.mjs")).default;
4
- };
5
-
6
- export {
7
- loadTopology
8
- };
9
- //# sourceMappingURL=chunk-CYCBMCOP.mjs.map