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