@dxos/react-ui-geo 0.8.4-main.67995b8 → 0.8.4-main.a4bbb77
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/dist/lib/browser/index.mjs +173 -145
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +173 -145
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Globe/Globe.d.ts +1 -1
- package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
- package/dist/types/src/components/Globe/Globe.stories.d.ts +25 -9
- package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
- package/dist/types/src/components/Map/Map.d.ts +25 -12
- package/dist/types/src/components/Map/Map.d.ts.map +1 -1
- package/dist/types/src/components/Map/Map.stories.d.ts +14 -8
- package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/context.d.ts +7 -7
- package/dist/types/src/hooks/context.d.ts.map +1 -1
- package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +1 -1
- package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useMapZoomHandler.d.ts +1 -1
- package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useSpinner.d.ts +1 -1
- package/dist/types/src/hooks/useSpinner.d.ts.map +1 -1
- package/dist/types/src/hooks/useTour.d.ts +4 -3
- package/dist/types/src/hooks/useTour.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +2 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/path.d.ts +5 -8
- package/dist/types/src/util/path.d.ts.map +1 -1
- package/dist/types/src/util/render.d.ts +4 -4
- package/dist/types/src/util/render.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -23
- package/src/components/Globe/Globe.stories.tsx +81 -33
- package/src/components/Globe/Globe.tsx +50 -30
- package/src/components/Map/Map.stories.tsx +25 -14
- package/src/components/Map/Map.tsx +206 -91
- package/src/components/Toolbar/Controls.tsx +2 -6
- package/src/components/index.ts +0 -2
- package/src/hooks/context.tsx +10 -10
- package/src/hooks/useGlobeZoomHandler.ts +3 -3
- package/src/hooks/useMapZoomHandler.ts +1 -1
- package/src/hooks/useSpinner.ts +2 -1
- package/src/hooks/useTour.ts +9 -8
- package/src/types.ts +3 -1
- package/src/util/inertia.ts +1 -1
- package/src/util/path.ts +5 -6
- package/src/util/render.ts +5 -3
- package/dist/types/src/components/types.d.ts +0 -15
- package/dist/types/src/components/types.d.ts.map +0 -1
- package/src/components/types.ts +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-geo",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.a4bbb77",
|
|
4
4
|
"description": "Geo components.",
|
|
5
5
|
"homepage": "https://github.com/dxos",
|
|
6
6
|
"bugs": "https://github.com/dxos/issues",
|
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"exports": {
|
|
12
12
|
"./data": {
|
|
13
|
+
"source": "./src/data.ts",
|
|
13
14
|
"types": "./dist/types/src/data.d.ts",
|
|
14
15
|
"browser": "./dist/lib/browser/data.mjs",
|
|
15
|
-
"node": "./dist/lib/node-esm/data.mjs"
|
|
16
|
-
"source": "./src/data.ts"
|
|
16
|
+
"node": "./dist/lib/node-esm/data.mjs"
|
|
17
17
|
},
|
|
18
18
|
".": {
|
|
19
|
+
"source": "./src/index.ts",
|
|
19
20
|
"types": "./dist/types/src/index.d.ts",
|
|
20
21
|
"browser": "./dist/lib/browser/index.mjs",
|
|
21
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
22
|
-
"source": "./src/index.ts"
|
|
22
|
+
"node": "./dist/lib/node-esm/index.mjs"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -37,31 +37,32 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@preact-signals/safe-react": "^0.9.0",
|
|
40
|
+
"@radix-ui/react-context": "^1.0.5",
|
|
40
41
|
"d3": "^7.9.0",
|
|
41
42
|
"d3-geo-projection": "^4.0.0",
|
|
42
43
|
"d3-hexbin": "^0.2.2",
|
|
43
44
|
"geojson": "^0.5.0",
|
|
44
45
|
"leaflet": "^1.9.4",
|
|
45
46
|
"lodash.defaultsdeep": "^4.6.1",
|
|
46
|
-
"react-leaflet": "^
|
|
47
|
+
"react-leaflet": "^5.0.0",
|
|
47
48
|
"react-resize-detector": "^11.0.1",
|
|
48
49
|
"topojson-client": "^3.1.0",
|
|
49
50
|
"topojson-simplify": "^3.0.3",
|
|
50
51
|
"versor": "^0.2.0",
|
|
51
|
-
"@dxos/async": "0.8.4-main.
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/node-std": "0.8.4-main.
|
|
55
|
-
"@dxos/util": "0.8.4-main.
|
|
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"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@react-three/drei": "^9.99.0",
|
|
59
|
-
"@react-three/fiber": "^
|
|
60
|
+
"@react-three/fiber": "^9.3.0",
|
|
60
61
|
"@types/d3": "^7.4.3",
|
|
61
62
|
"@types/geojson": "^7946.0.14",
|
|
62
63
|
"@types/leaflet": "^1.9.16",
|
|
63
|
-
"@types/react": "~
|
|
64
|
-
"@types/react-dom": "~
|
|
64
|
+
"@types/react": "~19.2.0",
|
|
65
|
+
"@types/react-dom": "~19.2.0",
|
|
65
66
|
"@types/three": "0.165.0",
|
|
66
67
|
"@types/topojson-client": "^3.1.4",
|
|
67
68
|
"@types/topojson-simplify": "^3.0.3",
|
|
@@ -69,18 +70,18 @@
|
|
|
69
70
|
"JSONStream": "^1.3.5",
|
|
70
71
|
"geojson2h3": "^1.2.0",
|
|
71
72
|
"leva": "^0.9.35",
|
|
72
|
-
"react": "~
|
|
73
|
-
"react-dom": "~
|
|
73
|
+
"react": "~19.2.0",
|
|
74
|
+
"react-dom": "~19.2.0",
|
|
74
75
|
"three": "0.165.0",
|
|
75
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
76
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
77
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
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"
|
|
78
79
|
},
|
|
79
80
|
"peerDependencies": {
|
|
80
|
-
"react": "
|
|
81
|
-
"react-dom": "
|
|
82
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
83
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
81
|
+
"react": "^19.0.0",
|
|
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"
|
|
84
85
|
},
|
|
85
86
|
"publishConfig": {
|
|
86
87
|
"access": "public"
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
// Copyright 2018 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
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 { withTheme
|
|
12
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
15
13
|
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
14
|
+
import { type Vector, useDrag, useGlobeZoomHandler, useSpinner, useTour } from '../../hooks';
|
|
15
|
+
import { type LatLngLiteral } from '../../types';
|
|
16
|
+
import { type StyleSet, closestPoint } from '../../util';
|
|
19
17
|
import { type ControlProps } from '../Toolbar';
|
|
20
18
|
|
|
19
|
+
import { Globe, type GlobeCanvasProps, type GlobeController, type GlobeRootProps } from './Globe';
|
|
20
|
+
|
|
21
21
|
// TODO(burdon): Load from JSON at runtime?
|
|
22
22
|
const useTopology = () => {
|
|
23
23
|
return useAsyncState(async () => (await import('../../../data/countries-110m.ts')).default);
|
|
@@ -95,8 +95,11 @@ const createTrip = (
|
|
|
95
95
|
routes: Record<string, string[]>,
|
|
96
96
|
points: Position[] = [],
|
|
97
97
|
) => {
|
|
98
|
-
let previousHub:
|
|
99
|
-
return Object.entries(routes).reduce<{
|
|
98
|
+
let previousHub: LatLngLiteral;
|
|
99
|
+
return Object.entries(routes).reduce<{
|
|
100
|
+
points: LatLngLiteral[];
|
|
101
|
+
lines: { source: LatLngLiteral; target: LatLngLiteral }[];
|
|
102
|
+
}>(
|
|
100
103
|
(features, [hub, regional]) => {
|
|
101
104
|
const hubAirport = airports.features.find(({ properties }) => properties.iata === hub);
|
|
102
105
|
if (hubAirport) {
|
|
@@ -125,7 +128,7 @@ const createTrip = (
|
|
|
125
128
|
);
|
|
126
129
|
};
|
|
127
130
|
|
|
128
|
-
type StoryProps = Pick<GlobeRootProps, '
|
|
131
|
+
type StoryProps = Pick<GlobeRootProps, 'zoom' | 'translation' | 'rotation'> &
|
|
129
132
|
Pick<GlobeCanvasProps, 'projection' | 'styles'> & {
|
|
130
133
|
drag?: boolean;
|
|
131
134
|
spin?: boolean;
|
|
@@ -133,8 +136,8 @@ type StoryProps = Pick<GlobeRootProps, 'scale' | 'translation' | 'rotation'> &
|
|
|
133
136
|
xAxis?: boolean;
|
|
134
137
|
};
|
|
135
138
|
|
|
136
|
-
const
|
|
137
|
-
|
|
139
|
+
const DefaultStory = ({
|
|
140
|
+
zoom: _zoom = 1,
|
|
138
141
|
translation,
|
|
139
142
|
rotation = [0, 0, 0],
|
|
140
143
|
projection,
|
|
@@ -154,6 +157,7 @@ const Story = ({
|
|
|
154
157
|
});
|
|
155
158
|
const [topology] = useTopology();
|
|
156
159
|
const [airports] = useAsyncState(async () => (await import('../../../data/airports.ts')).default);
|
|
160
|
+
|
|
157
161
|
const features = useMemo(() => {
|
|
158
162
|
return airports ? createTrip(airports, routes, (dots?.objects.dots as any)?.geometries[0].coordinates) : undefined;
|
|
159
163
|
}, [airports, routes, dots]);
|
|
@@ -188,18 +192,18 @@ const Story = ({
|
|
|
188
192
|
break;
|
|
189
193
|
}
|
|
190
194
|
case 'zoom-in': {
|
|
191
|
-
controller.current.
|
|
195
|
+
controller.current.setZoom((scale) => scale * 1.1);
|
|
192
196
|
break;
|
|
193
197
|
}
|
|
194
198
|
case 'zoom-out': {
|
|
195
|
-
controller.current.
|
|
199
|
+
controller.current.setZoom((scale) => scale * 0.9);
|
|
196
200
|
break;
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
};
|
|
200
204
|
|
|
201
205
|
return (
|
|
202
|
-
<Globe.Root classNames='absolute inset-0'
|
|
206
|
+
<Globe.Root classNames='absolute inset-0' zoom={_zoom} translation={translation} rotation={rotation}>
|
|
203
207
|
<Globe.Canvas
|
|
204
208
|
ref={controller}
|
|
205
209
|
topology={styles?.dots ? dots : topology}
|
|
@@ -219,11 +223,15 @@ const Story = ({
|
|
|
219
223
|
|
|
220
224
|
const initialRotation: Vector = [0, -40, 0];
|
|
221
225
|
|
|
222
|
-
const meta
|
|
226
|
+
const meta = {
|
|
223
227
|
title: 'ui/react-ui-geo/Globe',
|
|
224
228
|
component: Globe.Root,
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
render: DefaultStory,
|
|
230
|
+
decorators: [withTheme],
|
|
231
|
+
parameters: {
|
|
232
|
+
layout: 'fullscreen',
|
|
233
|
+
},
|
|
234
|
+
} satisfies Meta;
|
|
227
235
|
|
|
228
236
|
export default meta;
|
|
229
237
|
|
|
@@ -234,7 +242,7 @@ export const Earth1 = () => {
|
|
|
234
242
|
useDrag(controller);
|
|
235
243
|
|
|
236
244
|
return (
|
|
237
|
-
<Globe.Root
|
|
245
|
+
<Globe.Root zoom={1.2} rotation={[Math.random() * 360, 0, 0]}>
|
|
238
246
|
<Globe.Canvas ref={setController} topology={topology} styles={defaultStyles} />
|
|
239
247
|
<Globe.Zoom onAction={handleAction} />
|
|
240
248
|
</Globe.Root>
|
|
@@ -249,7 +257,7 @@ export const Earth2 = () => {
|
|
|
249
257
|
|
|
250
258
|
return (
|
|
251
259
|
<div className='absolute bottom-0 left-0 right-0 '>
|
|
252
|
-
<Globe.Root classNames='h-[400px]'
|
|
260
|
+
<Globe.Root classNames='h-[400px]' zoom={2.8} translation={{ x: 0, y: 400 }}>
|
|
253
261
|
<Globe.Canvas ref={setController} topology={topology} styles={defaultStyles} />
|
|
254
262
|
<Globe.Zoom onAction={handleAction} />
|
|
255
263
|
</Globe.Root>
|
|
@@ -283,33 +291,73 @@ export const Mercator = () => {
|
|
|
283
291
|
useDrag(controller);
|
|
284
292
|
|
|
285
293
|
return (
|
|
286
|
-
<Globe.Root classNames='flex grow overflow-hidden'
|
|
294
|
+
<Globe.Root classNames='flex grow overflow-hidden' zoom={0.7} rotation={initialRotation}>
|
|
287
295
|
<Globe.Canvas ref={setController} topology={topology} projection='mercator' styles={monochrome} />
|
|
288
296
|
<Globe.Zoom onAction={handleAction} />
|
|
289
297
|
</Globe.Root>
|
|
290
298
|
);
|
|
291
299
|
};
|
|
292
300
|
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
type Story = StoryObj<typeof DefaultStory>;
|
|
302
|
+
|
|
303
|
+
export const Globe1: Story = {
|
|
304
|
+
args: {
|
|
305
|
+
drag: true,
|
|
306
|
+
projection: 'mercator',
|
|
307
|
+
zoom: 0.8,
|
|
308
|
+
rotation: initialRotation,
|
|
309
|
+
styles: defaultStyles,
|
|
310
|
+
},
|
|
295
311
|
};
|
|
296
312
|
|
|
297
|
-
export const Globe2 =
|
|
298
|
-
|
|
313
|
+
export const Globe2: Story = {
|
|
314
|
+
args: {
|
|
315
|
+
drag: true,
|
|
316
|
+
projection: 'transverse-mercator',
|
|
317
|
+
zoom: 0.8,
|
|
318
|
+
rotation: initialRotation,
|
|
319
|
+
styles: defaultStyles,
|
|
320
|
+
},
|
|
299
321
|
};
|
|
300
322
|
|
|
301
|
-
export const Globe3 =
|
|
302
|
-
|
|
323
|
+
export const Globe3: Story = {
|
|
324
|
+
args: {
|
|
325
|
+
drag: true,
|
|
326
|
+
spin: true,
|
|
327
|
+
zoom: 1.5,
|
|
328
|
+
rotation: initialRotation,
|
|
329
|
+
styles: defaultStyles,
|
|
330
|
+
},
|
|
303
331
|
};
|
|
304
332
|
|
|
305
|
-
export const Globe4 =
|
|
306
|
-
|
|
333
|
+
export const Globe4: Story = {
|
|
334
|
+
args: {
|
|
335
|
+
drag: true,
|
|
336
|
+
tour: true,
|
|
337
|
+
zoom: 2,
|
|
338
|
+
rotation: initialRotation,
|
|
339
|
+
styles: defaultStyles,
|
|
340
|
+
},
|
|
307
341
|
};
|
|
308
342
|
|
|
309
|
-
export const Globe5 =
|
|
310
|
-
|
|
343
|
+
export const Globe5: Story = {
|
|
344
|
+
args: {
|
|
345
|
+
drag: true,
|
|
346
|
+
tour: true,
|
|
347
|
+
zoom: 0.9,
|
|
348
|
+
rotation: initialRotation,
|
|
349
|
+
styles: dotStyles,
|
|
350
|
+
},
|
|
311
351
|
};
|
|
312
352
|
|
|
313
|
-
export const Globe6 =
|
|
314
|
-
|
|
353
|
+
export const Globe6: Story = {
|
|
354
|
+
args: {
|
|
355
|
+
drag: true,
|
|
356
|
+
xAxis: true,
|
|
357
|
+
tour: true,
|
|
358
|
+
zoom: 2,
|
|
359
|
+
translation: { x: 0, y: 600 },
|
|
360
|
+
rotation: [0, -20, 0],
|
|
361
|
+
styles: dotStyles,
|
|
362
|
+
},
|
|
315
363
|
};
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
type GeoProjection,
|
|
7
|
+
easeLinear,
|
|
8
|
+
easeSinOut,
|
|
7
9
|
geoMercator,
|
|
8
10
|
geoOrthographic,
|
|
9
11
|
geoPath,
|
|
10
12
|
geoTransverseMercator,
|
|
11
13
|
interpolateNumber,
|
|
12
14
|
transition,
|
|
13
|
-
easeLinear,
|
|
14
|
-
easeSinOut,
|
|
15
15
|
} from 'd3';
|
|
16
16
|
import { type ControlPosition } from 'leaflet';
|
|
17
17
|
import React, {
|
|
@@ -26,7 +26,7 @@ import React, {
|
|
|
26
26
|
import { useResizeDetector } from 'react-resize-detector';
|
|
27
27
|
import { type Topology } from 'topojson-specification';
|
|
28
28
|
|
|
29
|
-
import { type
|
|
29
|
+
import { type ThemeMode, type ThemedClassName, useDynamicRef, useThemeContext } from '@dxos/react-ui';
|
|
30
30
|
import { mx } from '@dxos/react-ui-theme';
|
|
31
31
|
|
|
32
32
|
import {
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
renderLayers,
|
|
45
45
|
timer,
|
|
46
46
|
} from '../../util';
|
|
47
|
-
import {
|
|
47
|
+
import { ActionControls, type ControlProps, ZoomControls, controlPositions } from '../Toolbar';
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
|
|
@@ -103,7 +103,7 @@ const defaultStyles: Record<ThemeMode, StyleSet> = {
|
|
|
103
103
|
export type GlobeController = {
|
|
104
104
|
canvas: HTMLCanvasElement;
|
|
105
105
|
projection: GeoProjection;
|
|
106
|
-
} & Pick<GlobeContextType, '
|
|
106
|
+
} & Pick<GlobeContextType, 'zoom' | 'translation' | 'rotation' | 'setZoom' | 'setTranslation' | 'setRotation'>;
|
|
107
107
|
|
|
108
108
|
export type ProjectionType = 'orthographic' | 'mercator' | 'transverse-mercator';
|
|
109
109
|
|
|
@@ -154,6 +154,7 @@ type GlobeCanvasProps = {
|
|
|
154
154
|
* Basic globe renderer.
|
|
155
155
|
* https://github.com/topojson/world-atlas
|
|
156
156
|
*/
|
|
157
|
+
// TODO(burdon): Move controller to root.
|
|
157
158
|
const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
|
|
158
159
|
({ projection: _projection, topology, features, styles: _styles }, forwardRef) => {
|
|
159
160
|
const { themeMode } = useThemeContext();
|
|
@@ -173,15 +174,14 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
|
|
|
173
174
|
}, [topology, features, styles]);
|
|
174
175
|
|
|
175
176
|
// State.
|
|
176
|
-
const { size, center,
|
|
177
|
+
const { size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation } =
|
|
177
178
|
useGlobeContext();
|
|
178
|
-
|
|
179
|
-
const scaleRef = useDynamicRef(scale);
|
|
179
|
+
const zoomRef = useDynamicRef(zoom);
|
|
180
180
|
|
|
181
181
|
// Update rotation.
|
|
182
182
|
useEffect(() => {
|
|
183
183
|
if (center) {
|
|
184
|
-
|
|
184
|
+
setZoom(1);
|
|
185
185
|
setRotation(positionToRotation(geoToPosition(center)));
|
|
186
186
|
}
|
|
187
187
|
}, [center]);
|
|
@@ -193,25 +193,25 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
|
|
|
193
193
|
canvas,
|
|
194
194
|
projection,
|
|
195
195
|
center,
|
|
196
|
-
get
|
|
197
|
-
return
|
|
196
|
+
get zoom() {
|
|
197
|
+
return zoomRef.current;
|
|
198
198
|
},
|
|
199
199
|
translation,
|
|
200
200
|
rotation,
|
|
201
201
|
setCenter,
|
|
202
|
-
|
|
202
|
+
setZoom: (s) => {
|
|
203
203
|
if (typeof s === 'function') {
|
|
204
|
-
const is = interpolateNumber(
|
|
204
|
+
const is = interpolateNumber(zoomRef.current, s(zoomRef.current));
|
|
205
205
|
// Stop easing if already zooming.
|
|
206
206
|
transition()
|
|
207
207
|
.ease(zooming.current ? easeLinear : easeSinOut)
|
|
208
208
|
.duration(200)
|
|
209
|
-
.tween('scale', () => (t) =>
|
|
209
|
+
.tween('scale', () => (t) => setZoom(is(t)))
|
|
210
210
|
.on('end', () => {
|
|
211
211
|
zooming.current = false;
|
|
212
212
|
});
|
|
213
213
|
} else {
|
|
214
|
-
|
|
214
|
+
setZoom(s);
|
|
215
215
|
}
|
|
216
216
|
},
|
|
217
217
|
setTranslation,
|
|
@@ -232,14 +232,14 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
|
|
|
232
232
|
timer(() => {
|
|
233
233
|
// https://d3js.org/d3-geo/projection
|
|
234
234
|
projection
|
|
235
|
-
.scale((Math.min(size.width, size.height) / 2) *
|
|
235
|
+
.scale((Math.min(size.width, size.height) / 2) * zoom)
|
|
236
236
|
.translate([size.width / 2 + (translation?.x ?? 0), size.height / 2 + (translation?.y ?? 0)])
|
|
237
237
|
.rotate(rotation ?? [0, 0, 0]);
|
|
238
238
|
|
|
239
|
-
renderLayers(generator, layers,
|
|
239
|
+
renderLayers(generator, layers, zoom, styles);
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
|
-
}, [generator, size,
|
|
242
|
+
}, [generator, size, zoom, translation, rotation, layers]);
|
|
243
243
|
|
|
244
244
|
if (!size.width || !size.height) {
|
|
245
245
|
return null;
|
|
@@ -249,8 +249,12 @@ const GlobeCanvas = forwardRef<GlobeController, GlobeCanvasProps>(
|
|
|
249
249
|
},
|
|
250
250
|
);
|
|
251
251
|
|
|
252
|
+
//
|
|
253
|
+
// Debug
|
|
254
|
+
//
|
|
255
|
+
|
|
252
256
|
const GlobeDebug = ({ position = 'topleft' }: { position?: ControlPosition }) => {
|
|
253
|
-
const { size,
|
|
257
|
+
const { size, zoom, translation, rotation } = useGlobeContext();
|
|
254
258
|
return (
|
|
255
259
|
<div
|
|
256
260
|
className={mx(
|
|
@@ -259,12 +263,16 @@ const GlobeDebug = ({ position = 'topleft' }: { position?: ControlPosition }) =>
|
|
|
259
263
|
)}
|
|
260
264
|
>
|
|
261
265
|
<pre className='font-mono text-xs text-green-700'>
|
|
262
|
-
{JSON.stringify({ size,
|
|
266
|
+
{JSON.stringify({ size, zoom, translation, rotation }, null, 2)}
|
|
263
267
|
</pre>
|
|
264
268
|
</div>
|
|
265
269
|
);
|
|
266
270
|
};
|
|
267
271
|
|
|
272
|
+
//
|
|
273
|
+
// Panel
|
|
274
|
+
//
|
|
275
|
+
|
|
268
276
|
const GlobePanel = ({
|
|
269
277
|
position,
|
|
270
278
|
classNames,
|
|
@@ -273,25 +281,37 @@ const GlobePanel = ({
|
|
|
273
281
|
return <div className={mx('z-10 absolute overflow-hidden', controlPositions[position], classNames)}>{children}</div>;
|
|
274
282
|
};
|
|
275
283
|
|
|
284
|
+
//
|
|
285
|
+
// Controls
|
|
286
|
+
//
|
|
287
|
+
|
|
276
288
|
const CustomControl = ({ position, children }: PropsWithChildren<{ position: ControlPosition }>) => {
|
|
277
289
|
return <div className={mx('z-10 absolute overflow-hidden', controlPositions[position])}>{children}</div>;
|
|
278
290
|
};
|
|
279
291
|
|
|
280
292
|
type GlobeControlProps = { position?: ControlPosition } & Pick<ControlProps, 'onAction'>;
|
|
281
293
|
|
|
294
|
+
const GlobeZoom = ({ onAction, position = 'bottomleft', ...props }: GlobeControlProps) => (
|
|
295
|
+
<CustomControl position={position} {...props}>
|
|
296
|
+
<ZoomControls onAction={onAction} />
|
|
297
|
+
</CustomControl>
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const GlobeAction = ({ onAction, position = 'bottomright', ...props }: GlobeControlProps) => (
|
|
301
|
+
<CustomControl position={position} {...props}>
|
|
302
|
+
<ActionControls onAction={onAction} />
|
|
303
|
+
</CustomControl>
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
//
|
|
307
|
+
// Globe
|
|
308
|
+
//
|
|
309
|
+
|
|
282
310
|
export const Globe = {
|
|
283
311
|
Root: GlobeRoot,
|
|
284
312
|
Canvas: GlobeCanvas,
|
|
285
|
-
Zoom:
|
|
286
|
-
|
|
287
|
-
<ZoomControls onAction={onAction} />
|
|
288
|
-
</CustomControl>
|
|
289
|
-
),
|
|
290
|
-
Action: ({ onAction, position = 'bottomright', ...props }: GlobeControlProps) => (
|
|
291
|
-
<CustomControl position={position} {...props}>
|
|
292
|
-
<ActionControls onAction={onAction} />
|
|
293
|
-
</CustomControl>
|
|
294
|
-
),
|
|
313
|
+
Zoom: GlobeZoom,
|
|
314
|
+
Action: GlobeAction,
|
|
295
315
|
Debug: GlobeDebug,
|
|
296
316
|
Panel: GlobePanel,
|
|
297
317
|
};
|
|
@@ -2,45 +2,56 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type StoryObj, type Meta } from '@storybook/react-vite';
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React, { useState } from 'react';
|
|
9
7
|
|
|
10
|
-
import {
|
|
8
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
9
|
|
|
12
|
-
import { Map, type MapController } from './Map';
|
|
13
10
|
import { useMapZoomHandler } from '../../hooks';
|
|
14
|
-
import { type
|
|
11
|
+
import { type GeoMarker } from '../../types';
|
|
12
|
+
|
|
13
|
+
import { Map, type MapController } from './Map';
|
|
15
14
|
|
|
16
|
-
const DefaultStory = ({ markers = [] }: { markers?:
|
|
15
|
+
const DefaultStory = ({ markers = [] }: { markers?: GeoMarker[] }) => {
|
|
17
16
|
const [controller, setController] = useState<MapController>();
|
|
18
17
|
const handleZoomAction = useMapZoomHandler(controller);
|
|
19
18
|
|
|
20
19
|
return (
|
|
21
|
-
<Map.Root>
|
|
22
|
-
<Map.
|
|
20
|
+
<Map.Root ref={setController}>
|
|
21
|
+
<Map.Tiles />
|
|
22
|
+
<Map.Markers markers={markers} />
|
|
23
23
|
<Map.Zoom position='bottomleft' onAction={handleZoomAction} />
|
|
24
24
|
<Map.Action position='bottomright' />
|
|
25
25
|
</Map.Root>
|
|
26
26
|
);
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const meta
|
|
29
|
+
const meta = {
|
|
30
30
|
title: 'ui/react-ui-geo/Map',
|
|
31
|
+
component: Map.Root as any,
|
|
31
32
|
render: DefaultStory,
|
|
32
|
-
decorators: [withTheme
|
|
33
|
-
|
|
33
|
+
decorators: [withTheme],
|
|
34
|
+
parameters: {
|
|
35
|
+
layout: 'fullscreen',
|
|
36
|
+
},
|
|
37
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
34
38
|
|
|
35
39
|
export default meta;
|
|
36
40
|
|
|
37
|
-
type Story = StoryObj<typeof
|
|
41
|
+
type Story = StoryObj<typeof meta>;
|
|
38
42
|
|
|
39
43
|
export const Default: Story = {};
|
|
40
44
|
|
|
41
45
|
export const WithMarkers: Story = {
|
|
42
46
|
args: {
|
|
43
47
|
markers: [
|
|
48
|
+
{ id: 'los angeles', title: 'Los Angeles', location: { lat: 34.0522, lng: -118.2437 } },
|
|
49
|
+
{ id: 'new york', title: 'New York', location: { lat: 40.7128, lng: -74.006 } },
|
|
50
|
+
{ id: 'warsaw', title: 'Warsaw', location: { lat: 52.2297, lng: 21.0122 } },
|
|
51
|
+
{ id: 'london', title: 'London', location: { lat: 51.5074, lng: -0.1278 } },
|
|
52
|
+
{ id: 'toronto', title: 'Toronto', location: { lat: 43.6532, lng: -79.3832 } },
|
|
53
|
+
{ id: 'seattle', title: 'Seattle', location: { lat: 47.6062, lng: -122.3321 } },
|
|
54
|
+
{ id: 'barcelona', title: 'Barcelona', location: { lat: 41.3851, lng: 2.1734 } },
|
|
44
55
|
{ id: 'tokyo', title: 'Tokyo', location: { lat: 35.6762, lng: 139.6503 } },
|
|
45
56
|
{ id: 'sydney', title: 'Sydney', location: { lat: -33.8688, lng: 151.2093 } },
|
|
46
57
|
{ id: 'auckland', title: 'Auckland', location: { lat: -36.8509, lng: 174.7645 } },
|
|
@@ -56,6 +67,6 @@ export const WithMarkers: Story = {
|
|
|
56
67
|
{ id: 'phnom-penh', title: 'Phnom Penh', location: { lat: 11.5564, lng: 104.9282 } },
|
|
57
68
|
{ id: 'vientiane', title: 'Vientiane', location: { lat: 17.9757, lng: 102.6331 } },
|
|
58
69
|
{ id: 'yangon', title: 'Yangon', location: { lat: 16.8661, lng: 96.1951 } },
|
|
59
|
-
] as
|
|
70
|
+
] as GeoMarker[],
|
|
60
71
|
},
|
|
61
72
|
};
|