@dxos/react-ui-geo 0.8.4-main.fd6878d → 0.8.4-staging.ac66bdf99f

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 (39) hide show
  1. package/dist/lib/browser/index.mjs +380 -455
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +380 -455
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Globe/Globe.d.ts +5 -3
  8. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  9. package/dist/types/src/components/Globe/Globe.stories.d.ts +27 -9
  10. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Map/Map.d.ts +14 -11
  12. package/dist/types/src/components/Map/Map.d.ts.map +1 -1
  13. package/dist/types/src/components/Map/Map.stories.d.ts +13 -7
  14. package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
  16. package/dist/types/src/hooks/context.d.ts +1 -3
  17. package/dist/types/src/hooks/context.d.ts.map +1 -1
  18. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -1
  19. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -1
  20. package/dist/types/src/hooks/useTour.d.ts.map +1 -1
  21. package/dist/types/src/index.d.ts +1 -0
  22. package/dist/types/src/index.d.ts.map +1 -1
  23. package/dist/types/src/translations.d.ts +12 -0
  24. package/dist/types/src/translations.d.ts.map +1 -0
  25. package/dist/types/src/util/render.d.ts.map +1 -1
  26. package/dist/types/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +28 -24
  28. package/src/components/Globe/Globe.stories.tsx +68 -26
  29. package/src/components/Globe/Globe.tsx +55 -28
  30. package/src/components/Map/Map.stories.tsx +16 -13
  31. package/src/components/Map/Map.tsx +82 -71
  32. package/src/components/Toolbar/Controls.tsx +12 -14
  33. package/src/hooks/context.tsx +6 -29
  34. package/src/hooks/useGlobeZoomHandler.ts +8 -2
  35. package/src/hooks/useSpinner.ts +0 -1
  36. package/src/hooks/useTour.ts +1 -0
  37. package/src/index.ts +1 -0
  38. package/src/translations.ts +20 -0
  39. package/src/util/render.ts +0 -1
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-geo",
3
- "version": "0.8.4-main.fd6878d",
3
+ "version": "0.8.4-staging.ac66bdf99f",
4
4
  "description": "Geo components.",
5
5
  "homepage": "https://github.com/dxos",
6
6
  "bugs": "https://github.com/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
13
  "sideEffects": true,
@@ -36,52 +40,52 @@
36
40
  "src"
37
41
  ],
38
42
  "dependencies": {
39
- "@preact-signals/safe-react": "^0.9.0",
40
- "@radix-ui/react-context": "^1.0.5",
43
+ "@radix-ui/react-compose-refs": "1.1.1",
44
+ "@radix-ui/react-context": "1.1.1",
41
45
  "d3": "^7.9.0",
42
46
  "d3-geo-projection": "^4.0.0",
43
47
  "d3-hexbin": "^0.2.2",
44
48
  "geojson": "^0.5.0",
45
49
  "leaflet": "^1.9.4",
46
50
  "lodash.defaultsdeep": "^4.6.1",
47
- "react-leaflet": "^4.2.1",
51
+ "react-leaflet": "^5.0.0",
48
52
  "react-resize-detector": "^11.0.1",
49
53
  "topojson-client": "^3.1.0",
50
54
  "topojson-simplify": "^3.0.3",
51
55
  "versor": "^0.2.0",
52
- "@dxos/async": "0.8.4-main.fd6878d",
53
- "@dxos/debug": "0.8.4-main.fd6878d",
54
- "@dxos/log": "0.8.4-main.fd6878d",
55
- "@dxos/node-std": "0.8.4-main.fd6878d",
56
- "@dxos/util": "0.8.4-main.fd6878d"
56
+ "@dxos/log": "0.8.4-staging.ac66bdf99f",
57
+ "@dxos/async": "0.8.4-staging.ac66bdf99f",
58
+ "@dxos/debug": "0.8.4-staging.ac66bdf99f",
59
+ "@dxos/node-std": "0.8.4-staging.ac66bdf99f",
60
+ "@dxos/util": "0.8.4-staging.ac66bdf99f"
57
61
  },
58
62
  "devDependencies": {
59
- "@react-three/drei": "^9.99.0",
60
- "@react-three/fiber": "^8.15.0",
63
+ "@react-three/drei": "^10.7.7",
64
+ "@react-three/fiber": "^9.5.0",
61
65
  "@types/d3": "^7.4.3",
62
66
  "@types/geojson": "^7946.0.14",
63
67
  "@types/leaflet": "^1.9.16",
64
- "@types/react": "~18.2.0",
65
- "@types/react-dom": "~18.2.0",
68
+ "@types/react": "~19.2.7",
69
+ "@types/react-dom": "~19.2.3",
66
70
  "@types/three": "0.165.0",
67
71
  "@types/topojson-client": "^3.1.4",
68
72
  "@types/topojson-simplify": "^3.0.3",
69
73
  "@types/topojson-specification": "^1.0.5",
70
74
  "JSONStream": "^1.3.5",
71
75
  "geojson2h3": "^1.2.0",
72
- "leva": "^0.9.35",
73
- "react": "~18.2.0",
74
- "react-dom": "~18.2.0",
75
- "three": "0.165.0",
76
- "@dxos/react-ui": "0.8.4-main.fd6878d",
77
- "@dxos/react-ui-theme": "0.8.4-main.fd6878d",
78
- "@dxos/storybook-utils": "0.8.4-main.fd6878d"
76
+ "leva": "^0.10.1",
77
+ "react": "~19.2.3",
78
+ "react-dom": "~19.2.3",
79
+ "three": "^0.178.0",
80
+ "@dxos/storybook-utils": "0.8.4-staging.ac66bdf99f",
81
+ "@dxos/react-ui": "0.8.4-staging.ac66bdf99f",
82
+ "@dxos/ui-theme": "0.8.4-staging.ac66bdf99f"
79
83
  },
80
84
  "peerDependencies": {
81
- "react": "~18.2.0",
82
- "react-dom": "~18.2.0",
83
- "@dxos/react-ui-theme": "0.8.4-main.fd6878d",
84
- "@dxos/react-ui": "0.8.4-main.fd6878d"
85
+ "react": "~19.2.3",
86
+ "react-dom": "~19.2.3",
87
+ "@dxos/react-ui": "0.8.4-staging.ac66bdf99f",
88
+ "@dxos/ui-theme": "0.8.4-staging.ac66bdf99f"
85
89
  },
86
90
  "publishConfig": {
87
91
  "access": "public"
@@ -2,22 +2,19 @@
2
2
  // Copyright 2018 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import { type FeatureCollection, type Geometry, type Position } from 'geojson';
9
7
  import { Leva } from 'leva';
10
8
  import React, { useMemo, useRef, useState } from 'react';
11
9
  import { type Topology } from 'topojson-specification';
12
10
 
13
11
  import { useAsyncState } from '@dxos/react-ui';
14
- import { withLayout, withTheme } from '@dxos/storybook-utils';
12
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
13
 
16
14
  import { type Vector, useDrag, useGlobeZoomHandler, useSpinner, useTour } from '../../hooks';
17
15
  import { type LatLngLiteral } from '../../types';
18
16
  import { type StyleSet, closestPoint } from '../../util';
19
17
  import { type ControlProps } from '../Toolbar';
20
-
21
18
  import { Globe, type GlobeCanvasProps, type GlobeController, type GlobeRootProps } from './Globe';
22
19
 
23
20
  // TODO(burdon): Load from JSON at runtime?
@@ -130,7 +127,7 @@ const createTrip = (
130
127
  );
131
128
  };
132
129
 
133
- type StoryProps = Pick<GlobeRootProps, 'zoom' | 'translation' | 'rotation'> &
130
+ type DefaultStoryProps = Pick<GlobeRootProps, 'zoom' | 'translation' | 'rotation'> &
134
131
  Pick<GlobeCanvasProps, 'projection' | 'styles'> & {
135
132
  drag?: boolean;
136
133
  spin?: boolean;
@@ -138,8 +135,8 @@ type StoryProps = Pick<GlobeRootProps, 'zoom' | 'translation' | 'rotation'> &
138
135
  xAxis?: boolean;
139
136
  };
140
137
 
141
- const Story = ({
142
- zoom: _zoom = 1,
138
+ const DefaultStory = ({
139
+ zoom: zoomProp = 1,
143
140
  translation,
144
141
  rotation = [0, 0, 0],
145
142
  projection,
@@ -148,7 +145,7 @@ const Story = ({
148
145
  spin = false,
149
146
  tour = false,
150
147
  xAxis = false,
151
- }: StoryProps) => {
148
+ }: DefaultStoryProps) => {
152
149
  const controller = useRef<GlobeController>(null);
153
150
  const [dots] = useAsyncState(async () => {
154
151
  const points = (await import('../../../data/countries-dots-3.ts')).default;
@@ -159,6 +156,7 @@ const Story = ({
159
156
  });
160
157
  const [topology] = useTopology();
161
158
  const [airports] = useAsyncState(async () => (await import('../../../data/airports.ts')).default);
159
+
162
160
  const features = useMemo(() => {
163
161
  return airports ? createTrip(airports, routes, (dots?.objects.dots as any)?.geometries[0].coordinates) : undefined;
164
162
  }, [airports, routes, dots]);
@@ -204,13 +202,13 @@ const Story = ({
204
202
  };
205
203
 
206
204
  return (
207
- <Globe.Root classNames='absolute inset-0' zoom={_zoom} translation={translation} rotation={rotation}>
205
+ <Globe.Root zoom={zoomProp} translation={translation} rotation={rotation}>
208
206
  <Globe.Canvas
209
- ref={controller}
210
207
  topology={styles?.dots ? dots : topology}
211
208
  projection={projection}
212
209
  styles={styles}
213
210
  features={tour ? { points: features?.points ?? [] } : features}
211
+ ref={controller}
214
212
  />
215
213
  <Globe.Zoom onAction={handleAction} />
216
214
  <Globe.Action onAction={handleAction} />
@@ -224,11 +222,15 @@ const Story = ({
224
222
 
225
223
  const initialRotation: Vector = [0, -40, 0];
226
224
 
227
- const meta: Meta = {
225
+ const meta = {
228
226
  title: 'ui/react-ui-geo/Globe',
229
227
  component: Globe.Root,
230
- decorators: [withTheme, withLayout({ fullscreen: true, classNames: 'bg-[#000]' })],
231
- };
228
+ render: DefaultStory,
229
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
230
+ parameters: {
231
+ layout: 'fullscreen',
232
+ },
233
+ } satisfies Meta;
232
234
 
233
235
  export default meta;
234
236
 
@@ -295,26 +297,66 @@ export const Mercator = () => {
295
297
  );
296
298
  };
297
299
 
298
- export const Globe1 = () => {
299
- return <Story drag projection='mercator' zoom={0.8} rotation={initialRotation} styles={defaultStyles} />;
300
+ type Story = StoryObj<typeof DefaultStory>;
301
+
302
+ export const Globe1: Story = {
303
+ args: {
304
+ drag: true,
305
+ projection: 'mercator',
306
+ zoom: 0.8,
307
+ rotation: initialRotation,
308
+ styles: defaultStyles,
309
+ },
300
310
  };
301
311
 
302
- export const Globe2 = () => {
303
- return <Story drag projection='transverse-mercator' zoom={0.8} rotation={initialRotation} styles={defaultStyles} />;
312
+ export const Globe2: Story = {
313
+ args: {
314
+ drag: true,
315
+ projection: 'transverse-mercator',
316
+ zoom: 0.8,
317
+ rotation: initialRotation,
318
+ styles: defaultStyles,
319
+ },
304
320
  };
305
321
 
306
- export const Globe3 = () => {
307
- return <Story drag spin zoom={1.5} rotation={initialRotation} styles={defaultStyles} />;
322
+ export const Globe3: Story = {
323
+ args: {
324
+ drag: true,
325
+ spin: true,
326
+ zoom: 1.5,
327
+ rotation: initialRotation,
328
+ styles: defaultStyles,
329
+ },
308
330
  };
309
331
 
310
- export const Globe4 = () => {
311
- return <Story drag tour zoom={2} rotation={initialRotation} styles={defaultStyles} />;
332
+ export const Globe4: Story = {
333
+ args: {
334
+ drag: true,
335
+ tour: true,
336
+ zoom: 2,
337
+ rotation: initialRotation,
338
+ styles: defaultStyles,
339
+ },
312
340
  };
313
341
 
314
- export const Globe5 = () => {
315
- return <Story drag tour zoom={0.9} rotation={initialRotation} styles={dotStyles} />;
342
+ export const Globe5: Story = {
343
+ args: {
344
+ drag: true,
345
+ tour: true,
346
+ zoom: 0.9,
347
+ rotation: initialRotation,
348
+ styles: dotStyles,
349
+ },
316
350
  };
317
351
 
318
- export const Globe6 = () => {
319
- return <Story drag xAxis tour zoom={2} translation={{ x: 0, y: 600 }} rotation={[0, -20, 0]} styles={dotStyles} />;
352
+ export const Globe6: Story = {
353
+ args: {
354
+ drag: true,
355
+ xAxis: true,
356
+ tour: true,
357
+ zoom: 2,
358
+ translation: { x: 0, y: 600 },
359
+ rotation: [0, -20, 0],
360
+ styles: dotStyles,
361
+ },
320
362
  };
@@ -26,15 +26,17 @@ import React, {
26
26
  import { useResizeDetector } from 'react-resize-detector';
27
27
  import { type Topology } from 'topojson-specification';
28
28
 
29
- import { type ThemeMode, type ThemedClassName, useDynamicRef, useThemeContext } from '@dxos/react-ui';
30
- import { mx } from '@dxos/react-ui-theme';
31
-
32
29
  import {
33
- GlobeContextProvider,
34
- type GlobeContextProviderProps,
35
- type GlobeContextType,
36
- useGlobeContext,
37
- } from '../../hooks';
30
+ type ThemeMode,
31
+ type ThemedClassName,
32
+ useComposedRefs,
33
+ useControlledState,
34
+ useDynamicRef,
35
+ useThemeContext,
36
+ } from '@dxos/react-ui';
37
+ import { composable, composableProps, mx } from '@dxos/ui-theme';
38
+
39
+ import { GlobeContext, type GlobeContextType, type Point, type Vector, useGlobeContext } from '../../hooks';
38
40
  import {
39
41
  type Features,
40
42
  type StyleSet,
@@ -126,18 +128,43 @@ const getProjection = (type: GlobeCanvasProps['projection'] = 'orthographic'): G
126
128
  // Root
127
129
  //
128
130
 
129
- type GlobeRootProps = PropsWithChildren<ThemedClassName<GlobeContextProviderProps>>;
130
-
131
- const GlobeRoot = ({ classNames, children, ...props }: GlobeRootProps) => {
132
- const { ref, width, height } = useResizeDetector<HTMLDivElement>();
133
- return (
134
- <div ref={ref} className={mx('relative flex grow overflow-hidden', classNames)}>
135
- <GlobeContextProvider size={{ width, height }} {...props}>
136
- {children}
137
- </GlobeContextProvider>
138
- </div>
139
- );
140
- };
131
+ type GlobeRootProps = Partial<Pick<GlobeContextType, 'center' | 'zoom' | 'translation' | 'rotation'>>;
132
+
133
+ const GlobeRoot = composable<HTMLDivElement, GlobeRootProps>(
134
+ (
135
+ { children, center: centerProp, zoom: zoomProp, translation: translationProp, rotation: rotationProp, ...props },
136
+ forwardedRef,
137
+ ) => {
138
+ const localRef = useRef<HTMLDivElement>(null);
139
+ const composedRef = useComposedRefs<HTMLDivElement>(localRef, forwardedRef);
140
+ const { width, height } = useResizeDetector<HTMLDivElement>({ targetRef: localRef });
141
+
142
+ const [center, setCenter] = useControlledState(centerProp);
143
+ const [zoom, setZoom] = useControlledState(zoomProp ?? 4);
144
+ const [translation, setTranslation] = useControlledState<Point>(translationProp);
145
+ const [rotation, setRotation] = useControlledState<Vector>(rotationProp);
146
+
147
+ return (
148
+ <GlobeContext.Provider
149
+ value={{
150
+ size: { width, height },
151
+ center,
152
+ zoom,
153
+ translation,
154
+ rotation,
155
+ setCenter,
156
+ setZoom,
157
+ setTranslation,
158
+ setRotation,
159
+ }}
160
+ >
161
+ <div {...composableProps(props, { classNames: 'relative dx-container' })} ref={composedRef}>
162
+ {children}
163
+ </div>
164
+ </GlobeContext.Provider>
165
+ );
166
+ },
167
+ );
141
168
 
142
169
  //
143
170
  // Canvas
@@ -156,16 +183,16 @@ type GlobeCanvasProps = {
156
183
  */
157
184
  // TODO(burdon): Move controller to root.
158
185
  const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
159
- ({ projection: _projection, topology, features, styles: _styles }, forwardRef) => {
186
+ ({ projection: projectionProp, topology, features, styles: stylesProp }, forwardRef) => {
160
187
  const { themeMode } = useThemeContext();
161
- const styles = useMemo(() => _styles ?? defaultStyles[themeMode], [_styles, themeMode]);
188
+ const styles = useMemo(() => stylesProp ?? defaultStyles[themeMode], [stylesProp, themeMode]);
162
189
 
163
190
  // Canvas.
164
191
  const [canvas, setCanvas] = useState<HTMLCanvasElement>(null);
165
192
  const canvasRef = (canvas: HTMLCanvasElement) => setCanvas(canvas);
166
193
 
167
194
  // Projection.
168
- const projection = useMemo(() => getProjection(_projection), [_projection]);
195
+ const projection = useMemo(() => getProjection(projectionProp), [projectionProp]);
169
196
 
170
197
  // Layers.
171
198
  // TODO(burdon): Generate on the fly based on what is visible.
@@ -199,9 +226,9 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
199
226
  translation,
200
227
  rotation,
201
228
  setCenter,
202
- setZoom: (s) => {
203
- if (typeof s === 'function') {
204
- const is = interpolateNumber(zoomRef.current, s(zoomRef.current));
229
+ setZoom: (state) => {
230
+ if (typeof state === 'function') {
231
+ const is = interpolateNumber(zoomRef.current, state(zoomRef.current));
205
232
  // Stop easing if already zooming.
206
233
  transition()
207
234
  .ease(zooming.current ? easeLinear : easeSinOut)
@@ -211,7 +238,7 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
211
238
  zooming.current = false;
212
239
  });
213
240
  } else {
214
- setZoom(s);
241
+ setZoom(state);
215
242
  }
216
243
  },
217
244
  setTranslation,
@@ -258,7 +285,7 @@ const GlobeDebug = ({ position = 'topleft' }: { position?: ControlPosition }) =>
258
285
  return (
259
286
  <div
260
287
  className={mx(
261
- 'z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded',
288
+ 'z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded-sm',
262
289
  controlPositions[position],
263
290
  )}
264
291
  >
@@ -2,16 +2,13 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useState } from 'react';
9
7
 
10
- import { withLayout, withTheme } from '@dxos/storybook-utils';
8
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
9
 
12
10
  import { useMapZoomHandler } from '../../hooks';
13
11
  import { type GeoMarker } from '../../types';
14
-
15
12
  import { Map, type MapController } from './Map';
16
13
 
17
14
  const DefaultStory = ({ markers = [] }: { markers?: GeoMarker[] }) => {
@@ -19,24 +16,30 @@ const DefaultStory = ({ markers = [] }: { markers?: GeoMarker[] }) => {
19
16
  const handleZoomAction = useMapZoomHandler(controller);
20
17
 
21
18
  return (
22
- <Map.Root ref={setController}>
23
- <Map.Tiles />
24
- <Map.Markers markers={markers} />
25
- <Map.Zoom position='bottomleft' onAction={handleZoomAction} />
26
- <Map.Action position='bottomright' />
19
+ <Map.Root>
20
+ <Map.Content ref={setController}>
21
+ <Map.Tiles />
22
+ <Map.Markers markers={markers} />
23
+ <Map.Zoom position='bottomleft' onAction={handleZoomAction} />
24
+ <Map.Action position='bottomright' />
25
+ </Map.Content>
27
26
  </Map.Root>
28
27
  );
29
28
  };
30
29
 
31
- const meta: Meta<typeof DefaultStory> = {
30
+ const meta = {
32
31
  title: 'ui/react-ui-geo/Map',
32
+ component: Map.Root as any,
33
33
  render: DefaultStory,
34
- decorators: [withTheme, withLayout({ fullscreen: true })],
35
- };
34
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
35
+ parameters: {
36
+ layout: 'fullscreen',
37
+ },
38
+ } satisfies Meta<typeof DefaultStory>;
36
39
 
37
40
  export default meta;
38
41
 
39
- type Story = StoryObj<typeof DefaultStory>;
42
+ type Story = StoryObj<typeof meta>;
40
43
 
41
44
  export const Default: Story = {};
42
45