@dxos/react-ui-geo 0.8.4-main.a4bbb77 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-geo",
3
- "version": "0.8.4-main.a4bbb77",
3
+ "version": "0.8.4-main.ae835ea",
4
4
  "description": "Geo components.",
5
5
  "homepage": "https://github.com/dxos",
6
6
  "bugs": "https://github.com/dxos/issues",
@@ -49,11 +49,11 @@
49
49
  "topojson-client": "^3.1.0",
50
50
  "topojson-simplify": "^3.0.3",
51
51
  "versor": "^0.2.0",
52
- "@dxos/async": "0.8.4-main.a4bbb77",
53
- "@dxos/log": "0.8.4-main.a4bbb77",
54
- "@dxos/debug": "0.8.4-main.a4bbb77",
55
- "@dxos/node-std": "0.8.4-main.a4bbb77",
56
- "@dxos/util": "0.8.4-main.a4bbb77"
52
+ "@dxos/async": "0.8.4-main.ae835ea",
53
+ "@dxos/debug": "0.8.4-main.ae835ea",
54
+ "@dxos/log": "0.8.4-main.ae835ea",
55
+ "@dxos/node-std": "0.8.4-main.ae835ea",
56
+ "@dxos/util": "0.8.4-main.ae835ea"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@react-three/drei": "^9.99.0",
@@ -61,8 +61,8 @@
61
61
  "@types/d3": "^7.4.3",
62
62
  "@types/geojson": "^7946.0.14",
63
63
  "@types/leaflet": "^1.9.16",
64
- "@types/react": "~19.2.0",
65
- "@types/react-dom": "~19.2.0",
64
+ "@types/react": "~19.2.2",
65
+ "@types/react-dom": "~19.2.2",
66
66
  "@types/three": "0.165.0",
67
67
  "@types/topojson-client": "^3.1.4",
68
68
  "@types/topojson-simplify": "^3.0.3",
@@ -73,15 +73,15 @@
73
73
  "react": "~19.2.0",
74
74
  "react-dom": "~19.2.0",
75
75
  "three": "0.165.0",
76
- "@dxos/react-ui": "0.8.4-main.a4bbb77",
77
- "@dxos/react-ui-theme": "0.8.4-main.a4bbb77",
78
- "@dxos/storybook-utils": "0.8.4-main.a4bbb77"
76
+ "@dxos/storybook-utils": "0.8.4-main.ae835ea",
77
+ "@dxos/react-ui-theme": "0.8.4-main.ae835ea",
78
+ "@dxos/react-ui": "0.8.4-main.ae835ea"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "react": "^19.0.0",
82
82
  "react-dom": "^19.0.0",
83
- "@dxos/react-ui": "0.8.4-main.a4bbb77",
84
- "@dxos/react-ui-theme": "0.8.4-main.a4bbb77"
83
+ "@dxos/react-ui": "0.8.4-main.ae835ea",
84
+ "@dxos/react-ui-theme": "0.8.4-main.ae835ea"
85
85
  },
86
86
  "publishConfig": {
87
87
  "access": "public"
@@ -130,6 +130,7 @@ type GlobeRootProps = PropsWithChildren<ThemedClassName<GlobeContextProviderProp
130
130
 
131
131
  const GlobeRoot = ({ classNames, children, ...props }: GlobeRootProps) => {
132
132
  const { ref, width, height } = useResizeDetector<HTMLDivElement>();
133
+
133
134
  return (
134
135
  <div ref={ref} className={mx('relative flex grow overflow-hidden', classNames)}>
135
136
  <GlobeContextProvider size={{ width, height }} {...props}>
@@ -156,16 +157,16 @@ type GlobeCanvasProps = {
156
157
  */
157
158
  // TODO(burdon): Move controller to root.
158
159
  const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
159
- ({ projection: _projection, topology, features, styles: _styles }, forwardRef) => {
160
+ ({ projection: projectionParam, topology, features, styles: stylesParam }, forwardRef) => {
160
161
  const { themeMode } = useThemeContext();
161
- const styles = useMemo(() => _styles ?? defaultStyles[themeMode], [_styles, themeMode]);
162
+ const styles = useMemo(() => stylesParam ?? defaultStyles[themeMode], [stylesParam, themeMode]);
162
163
 
163
164
  // Canvas.
164
165
  const [canvas, setCanvas] = useState<HTMLCanvasElement>(null);
165
166
  const canvasRef = (canvas: HTMLCanvasElement) => setCanvas(canvas);
166
167
 
167
168
  // Projection.
168
- const projection = useMemo(() => getProjection(_projection), [_projection]);
169
+ const projection = useMemo(() => getProjection(projectionParam), [projectionParam]);
169
170
 
170
171
  // Layers.
171
172
  // TODO(burdon): Generate on the fly based on what is visible.
@@ -8,10 +8,8 @@ import { createContext } from '@radix-ui/react-context';
8
8
  import L, { Control, type ControlPosition, DomEvent, DomUtil, type LatLngLiteral, latLngBounds } from 'leaflet';
9
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';
11
+ import { MapContainer, type MapContainerProps, Marker, Popup, TileLayer, useMap, useMapEvents } from 'react-leaflet';
13
12
 
14
- import { debounce } from '@dxos/async';
15
13
  import { ThemeProvider, type ThemedClassName, Tooltip } from '@dxos/react-ui';
16
14
  import { defaultTx, mx } from '@dxos/react-ui-theme';
17
15
 
@@ -25,7 +23,7 @@ import { ActionControls, type ControlProps, ZoomControls, controlPositions } fro
25
23
  const defaults = {
26
24
  center: { lat: 51, lng: 0 } as L.LatLngLiteral,
27
25
  zoom: 4,
28
- };
26
+ } as const;
29
27
 
30
28
  //
31
29
  // Controller
@@ -42,6 +40,7 @@ type MapController = {
42
40
 
43
41
  type MapContextValue = {
44
42
  attention?: boolean;
43
+ onChange?: (ev: { center: LatLngLiteral; zoom: number }) => void;
45
44
  };
46
45
 
47
46
  const [MapContextProvier, useMapContext] = createContext<MapContextValue>('Map');
@@ -50,27 +49,14 @@ const [MapContextProvier, useMapContext] = createContext<MapContextValue>('Map')
50
49
  // Root
51
50
  //
52
51
 
53
- type MapRootProps = ThemedClassName<
54
- MapContainerProps & {
55
- onChange?: (ev: { center: LatLngLiteral; zoom: number }) => void;
56
- }
57
- >;
52
+ type MapRootProps = ThemedClassName<MapContainerProps & Pick<MapContextValue, 'onChange'>>;
58
53
 
59
54
  /**
60
55
  * https://react-leaflet.js.org/docs/api-map
61
56
  */
62
57
  const MapRoot = forwardRef<MapController, MapRootProps>(
63
58
  (
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
- },
59
+ { classNames, scrollWheelZoom = true, doubleClickZoom = true, touchZoom = true, center, zoom, onChange, ...props },
74
60
  forwardedRef,
75
61
  ) => {
76
62
  const [attention, setAttention] = useState(false);
@@ -90,32 +76,6 @@ const MapRoot = forwardRef<MapController, MapRootProps>(
90
76
  [],
91
77
  );
92
78
 
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
79
  // Enable/disable scroll wheel zoom.
120
80
  // TODO(burdon): Use attention:
121
81
  // const {hasAttention} = useAttention(props.id);
@@ -132,7 +92,7 @@ const MapRoot = forwardRef<MapController, MapRootProps>(
132
92
  }, [map, attention]);
133
93
 
134
94
  return (
135
- <MapContextProvier attention={attention}>
95
+ <MapContextProvier attention={attention} onChange={onChange}>
136
96
  <MapContainer
137
97
  {...props}
138
98
  ref={mapRef}
@@ -142,8 +102,8 @@ const MapRoot = forwardRef<MapController, MapRootProps>(
142
102
  scrollWheelZoom={scrollWheelZoom}
143
103
  doubleClickZoom={doubleClickZoom}
144
104
  touchZoom={touchZoom}
145
- center={center}
146
- zoom={zoom}
105
+ center={center ?? defaults.center}
106
+ zoom={zoom ?? defaults.zoom}
147
107
  // whenReady={() => {}}
148
108
  />
149
109
  </MapContextProvier>
@@ -162,6 +122,16 @@ type MapTilesProps = {};
162
122
 
163
123
  const MapTiles = (_props: MapTilesProps) => {
164
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
+ });
165
135
 
166
136
  // NOTE: Need to dynamically update data attribute since TileLayer doesn't update, but
167
137
  // Tailwind requires setting the property for static analysis.
@@ -26,6 +26,12 @@ export type GlobeContextType = {
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<
@@ -35,15 +41,15 @@ export type GlobeContextProviderProps = PropsWithChildren<
35
41
  export const GlobeContextProvider = ({
36
42
  children,
37
43
  size,
38
- center: _center,
39
- zoom: _zoom,
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 [zoom, setZoom] = useControlledState(_zoom);
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
@@ -6,6 +6,8 @@ import { useCallback } from 'react';
6
6
 
7
7
  import { type ControlProps, type GlobeController } from '../components';
8
8
 
9
+ const ZOOM_FACTOR = 0.1;
10
+
9
11
  export const useGlobeZoomHandler = (controller: GlobeController | null | undefined): ControlProps['onAction'] => {
10
12
  return useCallback<ControlProps['onAction']>(
11
13
  (event) => {
@@ -15,11 +17,15 @@ export const useGlobeZoomHandler = (controller: GlobeController | null | undefin
15
17
 
16
18
  switch (event) {
17
19
  case 'zoom-in': {
18
- controller.setZoom((zoom) => zoom * 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.setZoom((zoom) => zoom * 0.9);
26
+ controller.setZoom((zoom) => {
27
+ return zoom * (1 - ZOOM_FACTOR);
28
+ });
23
29
  break;
24
30
  }
25
31
  }