@dxos/react-ui-geo 0.7.5-labs.071a3e2

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 (126) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/data/airports.ts +52524 -0
  4. package/data/cities.ts +1211 -0
  5. package/data/countries-110m.ts +10587 -0
  6. package/data/countries-dots-3.ts +42989 -0
  7. package/data/countries-dots-4.ts +300941 -0
  8. package/data/raw/airports.json +39386 -0
  9. package/data/raw/countries-10m.json +1 -0
  10. package/data/raw/countries-110m.json +1 -0
  11. package/data/raw/countries-50m.json +1 -0
  12. package/data/raw/countries.json +1 -0
  13. package/dist/lib/browser/chunk-ENCWOTYX.mjs +9 -0
  14. package/dist/lib/browser/chunk-ENCWOTYX.mjs.map +7 -0
  15. package/dist/lib/browser/countries-110m-WI4PCLDF.mjs +37859 -0
  16. package/dist/lib/browser/countries-110m-WI4PCLDF.mjs.map +7 -0
  17. package/dist/lib/browser/data.mjs +7 -0
  18. package/dist/lib/browser/data.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +1020 -0
  20. package/dist/lib/browser/index.mjs.map +7 -0
  21. package/dist/lib/browser/meta.json +1 -0
  22. package/dist/lib/node/chunk-LAICG6L2.cjs +40 -0
  23. package/dist/lib/node/chunk-LAICG6L2.cjs.map +7 -0
  24. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs +37877 -0
  25. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs.map +7 -0
  26. package/dist/lib/node/data.cjs +28 -0
  27. package/dist/lib/node/data.cjs.map +7 -0
  28. package/dist/lib/node/index.cjs +1045 -0
  29. package/dist/lib/node/index.cjs.map +7 -0
  30. package/dist/lib/node/meta.json +1 -0
  31. package/dist/lib/node-esm/chunk-PIIEDZEU.mjs +11 -0
  32. package/dist/lib/node-esm/chunk-PIIEDZEU.mjs.map +7 -0
  33. package/dist/lib/node-esm/countries-110m-DQ4XRC4B.mjs +37861 -0
  34. package/dist/lib/node-esm/countries-110m-DQ4XRC4B.mjs.map +7 -0
  35. package/dist/lib/node-esm/data.mjs +8 -0
  36. package/dist/lib/node-esm/data.mjs.map +7 -0
  37. package/dist/lib/node-esm/index.mjs +1021 -0
  38. package/dist/lib/node-esm/index.mjs.map +7 -0
  39. package/dist/lib/node-esm/meta.json +1 -0
  40. package/dist/types/data/airports.d.ts +18 -0
  41. package/dist/types/data/airports.d.ts.map +1 -0
  42. package/dist/types/data/cities.d.ts +17 -0
  43. package/dist/types/data/cities.d.ts.map +1 -0
  44. package/dist/types/data/countries-110m.d.ts +36 -0
  45. package/dist/types/data/countries-110m.d.ts.map +1 -0
  46. package/dist/types/data/countries-dots-3.d.ts +9 -0
  47. package/dist/types/data/countries-dots-3.d.ts.map +1 -0
  48. package/dist/types/data/countries-dots-4.d.ts +9 -0
  49. package/dist/types/data/countries-dots-4.d.ts.map +1 -0
  50. package/dist/types/src/components/Globe/Globe.d.ts +37 -0
  51. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -0
  52. package/dist/types/src/components/Globe/Globe.stories.d.ts +15 -0
  53. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -0
  54. package/dist/types/src/components/Globe/index.d.ts +2 -0
  55. package/dist/types/src/components/Globe/index.d.ts.map +1 -0
  56. package/dist/types/src/components/Map/Map.d.ts +34 -0
  57. package/dist/types/src/components/Map/Map.d.ts.map +1 -0
  58. package/dist/types/src/components/Map/Map.stories.d.ts +7 -0
  59. package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -0
  60. package/dist/types/src/components/Map/index.d.ts +2 -0
  61. package/dist/types/src/components/Map/index.d.ts.map +1 -0
  62. package/dist/types/src/components/Toolbar/Controls.d.ts +11 -0
  63. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -0
  64. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  65. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  66. package/dist/types/src/components/index.d.ts +5 -0
  67. package/dist/types/src/components/index.d.ts.map +1 -0
  68. package/dist/types/src/components/types.d.ts +14 -0
  69. package/dist/types/src/components/types.d.ts.map +1 -0
  70. package/dist/types/src/data.d.ts +3 -0
  71. package/dist/types/src/data.d.ts.map +1 -0
  72. package/dist/types/src/hooks/context.d.ts +26 -0
  73. package/dist/types/src/hooks/context.d.ts.map +1 -0
  74. package/dist/types/src/hooks/index.d.ts +7 -0
  75. package/dist/types/src/hooks/index.d.ts.map +1 -0
  76. package/dist/types/src/hooks/useDrag.d.ts +16 -0
  77. package/dist/types/src/hooks/useDrag.d.ts.map +1 -0
  78. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +3 -0
  79. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -0
  80. package/dist/types/src/hooks/useMapZoomHandler.d.ts +3 -0
  81. package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -0
  82. package/dist/types/src/hooks/useSpinner.d.ts +11 -0
  83. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -0
  84. package/dist/types/src/hooks/useTour.d.ts +13 -0
  85. package/dist/types/src/hooks/useTour.d.ts.map +1 -0
  86. package/dist/types/src/index.d.ts +6 -0
  87. package/dist/types/src/index.d.ts.map +1 -0
  88. package/dist/types/src/types.d.ts +7 -0
  89. package/dist/types/src/types.d.ts.map +1 -0
  90. package/dist/types/src/util/debug.d.ts +2 -0
  91. package/dist/types/src/util/debug.d.ts.map +1 -0
  92. package/dist/types/src/util/index.d.ts +5 -0
  93. package/dist/types/src/util/index.d.ts.map +1 -0
  94. package/dist/types/src/util/inertia.d.ts +16 -0
  95. package/dist/types/src/util/inertia.d.ts.map +1 -0
  96. package/dist/types/src/util/path.d.ts +15 -0
  97. package/dist/types/src/util/path.d.ts.map +1 -0
  98. package/dist/types/src/util/render.d.ts +26 -0
  99. package/dist/types/src/util/render.d.ts.map +1 -0
  100. package/dist/types/tsconfig.tsbuildinfo +1 -0
  101. package/package.json +83 -0
  102. package/src/components/Globe/Globe.stories.tsx +318 -0
  103. package/src/components/Globe/Globe.tsx +270 -0
  104. package/src/components/Globe/index.ts +5 -0
  105. package/src/components/Map/Map.stories.tsx +39 -0
  106. package/src/components/Map/Map.tsx +203 -0
  107. package/src/components/Map/index.ts +5 -0
  108. package/src/components/Toolbar/Controls.tsx +71 -0
  109. package/src/components/Toolbar/index.ts +5 -0
  110. package/src/components/index.ts +9 -0
  111. package/src/components/types.ts +18 -0
  112. package/src/data.ts +9 -0
  113. package/src/hooks/context.tsx +59 -0
  114. package/src/hooks/index.ts +10 -0
  115. package/src/hooks/useDrag.ts +55 -0
  116. package/src/hooks/useGlobeZoomHandler.ts +29 -0
  117. package/src/hooks/useMapZoomHandler.ts +29 -0
  118. package/src/hooks/useSpinner.ts +69 -0
  119. package/src/hooks/useTour.ts +114 -0
  120. package/src/index.ts +9 -0
  121. package/src/types.ts +11 -0
  122. package/src/util/debug.ts +16 -0
  123. package/src/util/index.ts +8 -0
  124. package/src/util/inertia.ts +197 -0
  125. package/src/util/path.ts +56 -0
  126. package/src/util/render.ts +149 -0
@@ -0,0 +1,1020 @@
1
+ import {
2
+ loadTopology
3
+ } from "./chunk-ENCWOTYX.mjs";
4
+
5
+ // packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
6
+ import * as d37 from "d3";
7
+ import React3, { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo, useRef, useState as useState3 } from "react";
8
+ import { useResizeDetector } from "react-resize-detector";
9
+ import { useDynamicRef } from "@dxos/react-ui";
10
+ import { mx } from "@dxos/react-ui-theme";
11
+
12
+ // packages/ui/react-ui-geo/src/hooks/context.tsx
13
+ import React, { createContext, useContext } from "react";
14
+ import { raise } from "@dxos/debug";
15
+ import { useControlledValue } from "@dxos/react-ui";
16
+ var GlobeContext = /* @__PURE__ */ createContext(void 0);
17
+ var GlobeContextProvider = ({ children, size, center: _center, scale: _scale, translation: _translation, rotation: _rotation }) => {
18
+ const [center, setCenter] = useControlledValue(_center);
19
+ const [scale, setScale] = useControlledValue(_scale);
20
+ const [translation, setTranslation] = useControlledValue(_translation);
21
+ const [rotation, setRotation] = useControlledValue(_rotation);
22
+ return /* @__PURE__ */ React.createElement(GlobeContext.Provider, {
23
+ value: {
24
+ size,
25
+ center,
26
+ scale,
27
+ translation,
28
+ rotation,
29
+ setCenter,
30
+ setScale,
31
+ setTranslation,
32
+ setRotation
33
+ }
34
+ }, children);
35
+ };
36
+ var useGlobeContext = () => {
37
+ return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
38
+ };
39
+
40
+ // packages/ui/react-ui-geo/src/hooks/useDrag.ts
41
+ import * as d34 from "d3";
42
+ import { useEffect } from "react";
43
+
44
+ // packages/ui/react-ui-geo/src/util/debug.ts
45
+ var debug = false;
46
+ var timer = (cb) => {
47
+ const start = Date.now();
48
+ const data = cb();
49
+ const t = Date.now() - start / 1e3;
50
+ if (debug) {
51
+ console.log({
52
+ t,
53
+ data
54
+ });
55
+ }
56
+ return data;
57
+ };
58
+
59
+ // packages/ui/react-ui-geo/src/util/inertia.ts
60
+ import * as d3 from "d3";
61
+ import versor from "versor";
62
+ var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
63
+ var geoInertiaDrag = (target, render, projection, options) => {
64
+ if (!options) {
65
+ options = {};
66
+ }
67
+ if (target.node) {
68
+ target = target.node();
69
+ }
70
+ target = d3.select(target);
71
+ const inertia = geoInertiaDragHelper({
72
+ projection,
73
+ render: (rotation) => {
74
+ projection.rotate(rotation);
75
+ render && render();
76
+ },
77
+ axis: restrictAxis(options.xAxis ? [
78
+ true,
79
+ false,
80
+ false
81
+ ] : [
82
+ true,
83
+ true,
84
+ true
85
+ ]),
86
+ start: options.start,
87
+ move: options.move,
88
+ end: options.end,
89
+ stop: options.stop,
90
+ finish: options.finish,
91
+ time: options.time,
92
+ hold: options.hold
93
+ });
94
+ target.call(d3.drag().on("start", inertia.start).on("drag", inertia.move).on("end", inertia.end));
95
+ return inertia;
96
+ };
97
+ var geoInertiaDragHelper = (opt) => {
98
+ const projection = opt.projection;
99
+ let v0;
100
+ let r0;
101
+ let q0;
102
+ let v10;
103
+ let v11;
104
+ let q10;
105
+ const inertia = inertiaHelper({
106
+ axis: opt.axis,
107
+ start: () => {
108
+ v0 = versor.cartesian(projection.invert(inertia.position));
109
+ r0 = projection.rotate();
110
+ q0 = versor(r0);
111
+ opt.start && opt.start();
112
+ },
113
+ move: () => {
114
+ const inv = projection.rotate(r0).invert(inertia.position);
115
+ if (isNaN(inv[0])) {
116
+ return;
117
+ }
118
+ const v1 = versor.cartesian(inv);
119
+ const q1 = versor.multiply(q0, versor.delta(v0, v1));
120
+ const r1 = versor.rotation(q1);
121
+ const r2 = opt.axis(r0, r1);
122
+ opt.render(r2);
123
+ opt.move && opt.move();
124
+ },
125
+ end: () => {
126
+ v10 = versor.cartesian(projection.invert(inertia.position.map((d, i) => d - inertia.velocity[i] / 1e3)));
127
+ q10 = versor(projection.rotate());
128
+ v11 = versor.cartesian(projection.invert(inertia.position));
129
+ opt.end && opt.end();
130
+ },
131
+ stop: opt.stop,
132
+ finish: opt.finish,
133
+ render: (t) => {
134
+ const r1 = versor.rotation(versor.multiply(q10, versor.delta(v10, v11, t * 1e3)));
135
+ const r2 = opt.axis(r0, r1);
136
+ opt.render && opt.render(r2);
137
+ },
138
+ time: opt.time
139
+ });
140
+ return inertia;
141
+ };
142
+ function inertiaHelper(opt) {
143
+ const A = opt.time || 5e3;
144
+ const limit = 1.0001;
145
+ const B = -Math.log(1 - 1 / limit);
146
+ const inertia = {
147
+ position: [
148
+ 0,
149
+ 0
150
+ ],
151
+ velocity: [
152
+ 0,
153
+ 0
154
+ ],
155
+ timer: d3.timer(() => {
156
+ }),
157
+ time: 0,
158
+ t: 0,
159
+ start: function(ev) {
160
+ const position = [
161
+ ev.x,
162
+ ev.y
163
+ ];
164
+ inertia.position = position;
165
+ inertia.velocity = [
166
+ 0,
167
+ 0
168
+ ];
169
+ inertia.timer.stop();
170
+ this.classList.remove("inertia");
171
+ this.classList.add("dragging");
172
+ opt.start && opt.start.call(this, position);
173
+ },
174
+ move: function(ev) {
175
+ const position = [
176
+ ev.x,
177
+ ev.y
178
+ ];
179
+ const time = performance.now();
180
+ const deltaTime = time - inertia.time;
181
+ const decay = 1 - Math.exp(-deltaTime / 1e3);
182
+ inertia.velocity = inertia.velocity.map((d, i) => {
183
+ const deltaPos = position[i] - inertia.position[i];
184
+ const deltaTime2 = time - inertia.time;
185
+ return 1e3 * (1 - decay) * deltaPos / deltaTime2 + d * decay;
186
+ });
187
+ inertia.velocity = opt.axis([
188
+ 0,
189
+ 0
190
+ ], inertia.velocity);
191
+ inertia.time = time;
192
+ inertia.position = position;
193
+ opt.move && opt.move.call(this, position);
194
+ },
195
+ end: function(ev) {
196
+ this.classList.remove("dragging", "inertia");
197
+ const v = inertia.velocity;
198
+ if (v[0] * v[0] + v[1] * v[1] < 100) {
199
+ inertia.timer.stop();
200
+ return opt.stop && opt.stop();
201
+ }
202
+ const time = performance.now();
203
+ const deltaTime = time - inertia.time;
204
+ if (opt.hold === void 0) {
205
+ opt.hold = 100;
206
+ }
207
+ if (deltaTime >= opt.hold) {
208
+ inertia.timer.stop();
209
+ return opt.stop && opt.stop();
210
+ }
211
+ this.classList.add("inertia");
212
+ opt.end && opt.end();
213
+ const self = this;
214
+ inertia.timer.restart((e) => {
215
+ inertia.t = limit * (1 - Math.exp(-B * e / A));
216
+ opt.render && opt.render(inertia.t);
217
+ if (inertia.t > 1) {
218
+ inertia.timer.stop();
219
+ self.classList.remove("inertia");
220
+ inertia.velocity = [
221
+ 0,
222
+ 0
223
+ ];
224
+ inertia.t = 1;
225
+ opt.finish && opt.finish();
226
+ }
227
+ });
228
+ }
229
+ };
230
+ inertia.timer.stop();
231
+ return inertia;
232
+ }
233
+
234
+ // packages/ui/react-ui-geo/src/util/path.ts
235
+ import * as d32 from "d3";
236
+ var positionToRotation = ([lng, lat], tilt = 0) => [
237
+ -lng,
238
+ tilt - lat,
239
+ 0
240
+ ];
241
+ var geoToPosition = ({ lat, lng }) => [
242
+ lng,
243
+ lat
244
+ ];
245
+ var geoPoint = (point) => ({
246
+ type: "Point",
247
+ coordinates: geoToPosition(point)
248
+ });
249
+ var geoCircle2 = ({ lat, lng }, radius) => d32.geoCircle().radius(radius).center([
250
+ lng,
251
+ lat
252
+ ])();
253
+ var geoLine = (p1, p2) => ({
254
+ type: "LineString",
255
+ coordinates: [
256
+ [
257
+ p1.lng,
258
+ p1.lat
259
+ ],
260
+ [
261
+ p2.lng,
262
+ p2.lat
263
+ ]
264
+ ]
265
+ });
266
+ var closestPoint = (points, target) => {
267
+ if (points.length === 0) {
268
+ return target;
269
+ }
270
+ let closestPoint2 = points[0];
271
+ let minDistance = getDistance(points[0], target);
272
+ for (const point of points) {
273
+ const distance = getDistance(point, target);
274
+ if (distance < minDistance) {
275
+ minDistance = distance;
276
+ closestPoint2 = point;
277
+ }
278
+ }
279
+ return closestPoint2;
280
+ };
281
+ var getDistance = (point1, point2) => {
282
+ const dx = point1[0] - point2[0];
283
+ const dy = point1[1] - point2[1];
284
+ return Math.sqrt(dx * dx + dy * dy);
285
+ };
286
+
287
+ // packages/ui/react-ui-geo/src/util/render.ts
288
+ import * as d33 from "d3";
289
+ import * as topojson from "topojson-client";
290
+ var createLayers = (topology, features, styles) => {
291
+ const layers = [];
292
+ if (styles.water) {
293
+ layers.push({
294
+ styles: styles.water,
295
+ path: {
296
+ type: "Sphere"
297
+ }
298
+ });
299
+ }
300
+ if (styles.graticule) {
301
+ layers.push({
302
+ styles: styles.graticule,
303
+ path: d33.geoGraticule().step([
304
+ 6,
305
+ 6
306
+ ])()
307
+ });
308
+ }
309
+ if (topology) {
310
+ if (topology.objects.land && styles.land) {
311
+ layers.push({
312
+ styles: styles.land,
313
+ path: topojson.feature(topology, topology.objects.land)
314
+ });
315
+ }
316
+ if (topology.objects.countries && styles.border) {
317
+ layers.push({
318
+ styles: styles.border,
319
+ path: topojson.mesh(topology, topology.objects.countries, (a, b) => a !== b)
320
+ });
321
+ }
322
+ if (topology.objects.dots && styles.dots) {
323
+ layers.push({
324
+ styles: styles.dots,
325
+ path: topology.objects.dots
326
+ });
327
+ }
328
+ }
329
+ if (features) {
330
+ const { points, lines } = features;
331
+ if (points && styles.point) {
332
+ layers.push({
333
+ styles: styles.point,
334
+ path: {
335
+ type: "GeometryCollection",
336
+ geometries: points.map((point) => geoPoint(point))
337
+ }
338
+ });
339
+ }
340
+ if (lines && styles.line) {
341
+ layers.push({
342
+ styles: styles.line,
343
+ path: {
344
+ type: "GeometryCollection",
345
+ geometries: lines.map(({ source, target }) => geoLine(source, target))
346
+ }
347
+ });
348
+ }
349
+ }
350
+ return layers;
351
+ };
352
+ var renderLayers = (generator, layers = [], scale) => {
353
+ const context = generator.context();
354
+ const { canvas: { width, height } } = context;
355
+ context.reset();
356
+ context.clearRect(0, 0, width, height);
357
+ layers.forEach(({ path, styles }) => {
358
+ context.save();
359
+ let fill = false;
360
+ let stroke = false;
361
+ if (styles) {
362
+ Object.entries(styles).forEach(([key, value]) => {
363
+ if (key === "pointRadius") {
364
+ generator.pointRadius(value * scale);
365
+ } else {
366
+ context[key] = value;
367
+ fill ||= key === "fillStyle";
368
+ stroke ||= key === "strokeStyle";
369
+ }
370
+ });
371
+ }
372
+ context.beginPath();
373
+ generator(path);
374
+ fill && context.fill();
375
+ stroke && context.stroke();
376
+ context.restore();
377
+ });
378
+ return context;
379
+ };
380
+
381
+ // packages/ui/react-ui-geo/src/hooks/useDrag.ts
382
+ var useDrag = (controller, options = {}) => {
383
+ useEffect(() => {
384
+ const canvas = controller?.canvas;
385
+ if (!canvas || options.disabled) {
386
+ return;
387
+ }
388
+ geoInertiaDrag(d34.select(canvas), () => {
389
+ controller.setRotation(controller.projection.rotate());
390
+ options.onUpdate?.({
391
+ type: "move",
392
+ controller
393
+ });
394
+ }, controller.projection, {
395
+ xAxis: options.xAxis,
396
+ time: 3e3,
397
+ start: () => options.onUpdate?.({
398
+ type: "start",
399
+ controller
400
+ }),
401
+ finish: () => options.onUpdate?.({
402
+ type: "end",
403
+ controller
404
+ })
405
+ });
406
+ return () => {
407
+ cancelDrag(d34.select(canvas));
408
+ };
409
+ }, [
410
+ controller,
411
+ options
412
+ ]);
413
+ };
414
+ var cancelDrag = (node) => node.on(".drag", null);
415
+
416
+ // packages/ui/react-ui-geo/src/hooks/useGlobeZoomHandler.ts
417
+ import { useCallback } from "react";
418
+ var useGlobeZoomHandler = (controller) => {
419
+ return useCallback((event) => {
420
+ if (!controller) {
421
+ return;
422
+ }
423
+ switch (event) {
424
+ case "zoom-in": {
425
+ controller.setScale((scale) => scale * 1.1);
426
+ break;
427
+ }
428
+ case "zoom-out": {
429
+ controller.setScale((scale) => scale * 0.9);
430
+ break;
431
+ }
432
+ }
433
+ }, [
434
+ controller
435
+ ]);
436
+ };
437
+
438
+ // packages/ui/react-ui-geo/src/hooks/useMapZoomHandler.ts
439
+ import { useCallback as useCallback2 } from "react";
440
+ var useMapZoomHandler = (controller) => {
441
+ return useCallback2((event) => {
442
+ if (!controller) {
443
+ return;
444
+ }
445
+ switch (event) {
446
+ case "zoom-in": {
447
+ controller.setZoom((scale) => scale + 1);
448
+ break;
449
+ }
450
+ case "zoom-out": {
451
+ controller.setZoom((scale) => scale - 1);
452
+ break;
453
+ }
454
+ }
455
+ }, [
456
+ controller
457
+ ]);
458
+ };
459
+
460
+ // packages/ui/react-ui-geo/src/hooks/useSpinner.ts
461
+ import * as d35 from "d3";
462
+ import { useEffect as useEffect2, useState } from "react";
463
+ var useSpinner = (controller, options = {}) => {
464
+ const [running, setRunning] = useState(false);
465
+ useEffect2(() => {
466
+ let timer4;
467
+ const start = () => {
468
+ const delta = options.delta ?? [
469
+ 1e-3,
470
+ 0,
471
+ 0
472
+ ];
473
+ let t = 0;
474
+ let lastRotation = controller.projection.rotate();
475
+ timer4 = d35.timer((elapsed) => {
476
+ const dt = elapsed - t;
477
+ t = elapsed;
478
+ const rotation = [
479
+ lastRotation[0] + delta[0] * dt,
480
+ lastRotation[1] + delta[1] * dt,
481
+ lastRotation[2] + delta[2] * dt
482
+ ];
483
+ lastRotation = rotation;
484
+ controller.setRotation(rotation);
485
+ });
486
+ };
487
+ const stop = () => {
488
+ if (timer4) {
489
+ timer4.stop();
490
+ timer4 = void 0;
491
+ }
492
+ };
493
+ if (controller && running) {
494
+ start();
495
+ } else {
496
+ stop();
497
+ }
498
+ return () => stop();
499
+ }, [
500
+ controller,
501
+ running
502
+ ]);
503
+ return [
504
+ () => {
505
+ if (!options.disabled) {
506
+ setRunning(true);
507
+ }
508
+ },
509
+ () => setRunning(false)
510
+ ];
511
+ };
512
+
513
+ // packages/ui/react-ui-geo/src/hooks/useTour.ts
514
+ import * as d36 from "d3";
515
+ import { useEffect as useEffect3, useState as useState2 } from "react";
516
+ import versor2 from "versor";
517
+ var TRANSITION_NAME = "globe-tour";
518
+ var defaultDuration = 1500;
519
+ var useTour = (controller, features, options = {}) => {
520
+ const selection2 = d36.selection();
521
+ const [running, setRunning] = useState2(false);
522
+ useEffect3(() => {
523
+ if (!running) {
524
+ selection2.interrupt(TRANSITION_NAME);
525
+ return;
526
+ }
527
+ let t;
528
+ if (controller && running) {
529
+ t = setTimeout(async () => {
530
+ const { canvas, projection, setRotation } = controller;
531
+ const context = canvas.getContext("2d", {
532
+ alpha: false
533
+ });
534
+ const path = d36.geoPath(projection, context).pointRadius(2);
535
+ const tilt = 0;
536
+ let last;
537
+ try {
538
+ for (const next of features.points) {
539
+ if (!running) {
540
+ break;
541
+ }
542
+ const p1 = last ? geoToPosition(last) : void 0;
543
+ const p2 = geoToPosition(next);
544
+ const ip = d36.geoInterpolate(p1 || p2, p2);
545
+ const distance = d36.geoDistance(p1 || p2, p2);
546
+ const r1 = p1 ? positionToRotation(p1, tilt) : controller.projection.rotate();
547
+ const r2 = positionToRotation(p2, tilt);
548
+ const iv = versor2.interpolate(r1, r2);
549
+ const transition2 = selection2.transition(TRANSITION_NAME).duration(Math.max(options.duration ?? defaultDuration, distance * 2e3)).tween("render", () => (t2) => {
550
+ const t1 = Math.max(0, Math.min(1, t2 * 2 - 1));
551
+ const t22 = Math.min(1, t2 * 2);
552
+ context.save();
553
+ {
554
+ context.beginPath();
555
+ context.strokeStyle = options?.styles?.arc?.strokeStyle ?? "yellow";
556
+ context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.scale ?? 1);
557
+ context.setLineDash(options?.styles?.arc?.lineDash ?? []);
558
+ path({
559
+ type: "LineString",
560
+ coordinates: [
561
+ ip(t1),
562
+ ip(t22)
563
+ ]
564
+ });
565
+ context.stroke();
566
+ context.beginPath();
567
+ context.fillStyle = options?.styles?.cursor?.fillStyle ?? "orange";
568
+ path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.scale ?? 1));
569
+ path({
570
+ type: "Point",
571
+ coordinates: ip(t22)
572
+ });
573
+ context.fill();
574
+ }
575
+ context.restore();
576
+ projection.rotate(iv(t2));
577
+ setRotation(projection.rotate());
578
+ });
579
+ await transition2.end();
580
+ last = next;
581
+ }
582
+ } catch (err) {
583
+ setRunning(false);
584
+ }
585
+ });
586
+ return () => {
587
+ clearTimeout(t);
588
+ selection2.interrupt(TRANSITION_NAME);
589
+ };
590
+ }
591
+ }, [
592
+ controller,
593
+ running
594
+ ]);
595
+ return [
596
+ () => {
597
+ if (!options.disabled) {
598
+ setRunning(true);
599
+ }
600
+ },
601
+ () => setRunning(false)
602
+ ];
603
+ };
604
+
605
+ // packages/ui/react-ui-geo/src/components/Toolbar/Controls.tsx
606
+ import React2 from "react";
607
+ import { IconButton, Toolbar } from "@dxos/react-ui";
608
+ var controlPositions = {
609
+ topleft: "top-2 left-2",
610
+ topright: "top-2 right-2",
611
+ bottomleft: "bottom-2 left-2",
612
+ bottomright: "bottom-2 right-2"
613
+ };
614
+ var ZoomControls = ({ classNames, onAction }) => {
615
+ return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
616
+ classNames: [
617
+ "gap-1",
618
+ classNames
619
+ ]
620
+ }, /* @__PURE__ */ React2.createElement(IconButton, {
621
+ //
622
+ icon: "ph--plus--regular",
623
+ label: "zoom in",
624
+ iconOnly: true,
625
+ size: 5,
626
+ classNames: "px-0 aspect-square",
627
+ onClick: () => onAction?.("zoom-in")
628
+ }), /* @__PURE__ */ React2.createElement(IconButton, {
629
+ //
630
+ icon: "ph--minus--regular",
631
+ label: "zoom out",
632
+ iconOnly: true,
633
+ size: 5,
634
+ classNames: "px-0 aspect-square",
635
+ onClick: () => onAction?.("zoom-out")
636
+ }));
637
+ };
638
+ var ActionControls = ({ classNames, onAction }) => {
639
+ return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
640
+ classNames: [
641
+ "gap-1",
642
+ classNames
643
+ ]
644
+ }, /* @__PURE__ */ React2.createElement(IconButton, {
645
+ //
646
+ icon: "ph--play--regular",
647
+ label: "start",
648
+ iconOnly: true,
649
+ size: 5,
650
+ classNames: "px-0 aspect-square",
651
+ onClick: () => onAction?.("start")
652
+ }), /* @__PURE__ */ React2.createElement(IconButton, {
653
+ //
654
+ icon: "ph--globe-hemisphere-west--regular",
655
+ label: "toggle",
656
+ iconOnly: true,
657
+ size: 5,
658
+ classNames: "px-0 aspect-square",
659
+ onClick: () => onAction?.("toggle")
660
+ }));
661
+ };
662
+
663
+ // packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
664
+ var defaultStyles = {
665
+ background: {
666
+ fillStyle: "#111111"
667
+ },
668
+ water: {
669
+ fillStyle: "#123E6A"
670
+ },
671
+ hex: {
672
+ strokeStyle: "green",
673
+ fillStyle: "gray",
674
+ pointRadius: 1
675
+ },
676
+ land: {
677
+ fillStyle: "#032153"
678
+ },
679
+ line: {
680
+ strokeStyle: "#111111"
681
+ },
682
+ point: {
683
+ fillStyle: "#111111",
684
+ strokeStyle: "#111111",
685
+ strokeWidth: 1,
686
+ pointRadius: 0.5
687
+ }
688
+ };
689
+ var projectionMap = {
690
+ orthographic: d37.geoOrthographic,
691
+ mercator: d37.geoMercator,
692
+ "transverse-mercator": d37.geoTransverseMercator
693
+ };
694
+ var getProjection = (type = "orthographic") => {
695
+ if (typeof type === "string") {
696
+ const constructor = projectionMap[type] ?? d37.geoOrthographic;
697
+ return constructor();
698
+ }
699
+ return type ?? d37.geoOrthographic();
700
+ };
701
+ var GlobeRoot = ({ classNames, children, ...props }) => {
702
+ const { ref, width, height } = useResizeDetector();
703
+ return /* @__PURE__ */ React3.createElement("div", {
704
+ ref,
705
+ className: mx("relative flex grow overflow-hidden", classNames)
706
+ }, /* @__PURE__ */ React3.createElement(GlobeContextProvider, {
707
+ size: {
708
+ width,
709
+ height
710
+ },
711
+ ...props
712
+ }, children));
713
+ };
714
+ var GlobeCanvas = /* @__PURE__ */ forwardRef(({ projection: _projection, topology, features, styles = defaultStyles }, forwardRef3) => {
715
+ const [canvas, setCanvas] = useState3(null);
716
+ const canvasRef = (canvas2) => setCanvas(canvas2);
717
+ const projection = useMemo(() => getProjection(_projection), [
718
+ _projection
719
+ ]);
720
+ const layers = useMemo(() => {
721
+ return timer(() => createLayers(topology, features, styles));
722
+ }, [
723
+ topology,
724
+ features,
725
+ styles
726
+ ]);
727
+ const { size, center, scale, translation, rotation, setCenter, setScale, setTranslation, setRotation } = useGlobeContext();
728
+ const scaleRef = useDynamicRef(scale);
729
+ useEffect4(() => {
730
+ if (center) {
731
+ setScale(1);
732
+ setRotation(positionToRotation(geoToPosition(center)));
733
+ }
734
+ }, [
735
+ center
736
+ ]);
737
+ const zooming = useRef(false);
738
+ useImperativeHandle(forwardRef3, () => {
739
+ return {
740
+ canvas,
741
+ projection,
742
+ center,
743
+ get scale() {
744
+ return scaleRef.current;
745
+ },
746
+ translation,
747
+ rotation,
748
+ setCenter,
749
+ setScale: (s) => {
750
+ if (typeof s === "function") {
751
+ const is = d37.interpolateNumber(scaleRef.current, s(scaleRef.current));
752
+ d37.transition().ease(zooming.current ? d37.easeLinear : d37.easeSinOut).duration(200).tween("scale", () => (t) => setScale(is(t))).on("end", () => {
753
+ zooming.current = false;
754
+ });
755
+ } else {
756
+ setScale(s);
757
+ }
758
+ },
759
+ setTranslation,
760
+ setRotation
761
+ };
762
+ }, [
763
+ canvas
764
+ ]);
765
+ const generator = useMemo(() => canvas && projection && d37.geoPath(projection, canvas.getContext("2d", {
766
+ alpha: false
767
+ })), [
768
+ canvas,
769
+ projection
770
+ ]);
771
+ useEffect4(() => {
772
+ if (canvas && projection) {
773
+ timer(() => {
774
+ projection.scale(Math.min(size.width, size.height) / 2 * scale).translate([
775
+ size.width / 2 + (translation?.x ?? 0),
776
+ size.height / 2 + (translation?.y ?? 0)
777
+ ]).rotate(rotation ?? [
778
+ 0,
779
+ 0,
780
+ 0
781
+ ]);
782
+ renderLayers(generator, layers, scale);
783
+ });
784
+ }
785
+ }, [
786
+ generator,
787
+ size,
788
+ scale,
789
+ translation,
790
+ rotation,
791
+ layers
792
+ ]);
793
+ if (!size.width || !size.height) {
794
+ return null;
795
+ }
796
+ return /* @__PURE__ */ React3.createElement("canvas", {
797
+ ref: canvasRef,
798
+ width: size.width,
799
+ height: size.height
800
+ });
801
+ });
802
+ var GlobeDebug = ({ position = "topleft" }) => {
803
+ const { size, scale, translation, rotation } = useGlobeContext();
804
+ return /* @__PURE__ */ React3.createElement("div", {
805
+ className: mx("z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded", controlPositions[position])
806
+ }, /* @__PURE__ */ React3.createElement("pre", {
807
+ className: "font-mono text-xs text-green-700"
808
+ }, JSON.stringify({
809
+ size,
810
+ scale,
811
+ translation,
812
+ rotation
813
+ }, null, 2)));
814
+ };
815
+ var GlobePanel = ({ position, classNames, children }) => {
816
+ return /* @__PURE__ */ React3.createElement("div", {
817
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
818
+ }, children);
819
+ };
820
+ var CustomControl = ({ position, children }) => {
821
+ return /* @__PURE__ */ React3.createElement("div", {
822
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position])
823
+ }, children);
824
+ };
825
+ var Globe = {
826
+ Root: GlobeRoot,
827
+ Canvas: GlobeCanvas,
828
+ Zoom: ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl, {
829
+ position,
830
+ ...props
831
+ }, /* @__PURE__ */ React3.createElement(ZoomControls, {
832
+ onAction
833
+ })),
834
+ Action: ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl, {
835
+ position,
836
+ ...props
837
+ }, /* @__PURE__ */ React3.createElement(ActionControls, {
838
+ onAction
839
+ })),
840
+ Debug: GlobeDebug,
841
+ Panel: GlobePanel
842
+ };
843
+
844
+ // packages/ui/react-ui-geo/src/components/Map/Map.tsx
845
+ import "leaflet/dist/leaflet.css";
846
+ import { Control, DomEvent, DomUtil, latLngBounds } from "leaflet";
847
+ import React4, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2 } from "react";
848
+ import { createRoot } from "react-dom/client";
849
+ import { MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet";
850
+ import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
851
+ import { debounce } from "@dxos/async";
852
+ import { Tooltip, ThemeProvider } from "@dxos/react-ui";
853
+ import { defaultTx, mx as mx2 } from "@dxos/react-ui-theme";
854
+ var defaults = {
855
+ // TODO(burdon): Guess location.
856
+ center: {
857
+ lat: 51,
858
+ lng: 0
859
+ },
860
+ zoom: 4
861
+ };
862
+ var MapRoot = ({ classNames, center = defaults.center, zoom = defaults.zoom, ...props }) => {
863
+ return /* @__PURE__ */ React4.createElement(MapContainer, {
864
+ className: mx2("relative flex w-full h-full grow bg-baseSurface", classNames),
865
+ attributionControl: false,
866
+ // TODO(burdon): Only if attention.
867
+ scrollWheelZoom: true,
868
+ zoomControl: false,
869
+ center,
870
+ zoom,
871
+ ...props
872
+ });
873
+ };
874
+ var MapCanvas = /* @__PURE__ */ forwardRef2(({ markers = [], center, zoom, onChange }, forwardedRef) => {
875
+ const { ref, width, height } = useResizeDetector2({
876
+ refreshRate: 200
877
+ });
878
+ const map = useMap();
879
+ useImperativeHandle2(forwardedRef, () => ({
880
+ setCenter: (center2, zoom2) => {
881
+ map.setView(center2, zoom2);
882
+ },
883
+ setZoom: (cb) => {
884
+ map.setZoom(cb(map.getZoom()));
885
+ }
886
+ }), [
887
+ map
888
+ ]);
889
+ useEffect5(() => {
890
+ if (width && height) {
891
+ map.invalidateSize();
892
+ }
893
+ }, [
894
+ width,
895
+ height
896
+ ]);
897
+ useEffect5(() => {
898
+ if (center) {
899
+ map.setView(center, zoom);
900
+ } else if (zoom !== void 0) {
901
+ map.setZoom(zoom);
902
+ }
903
+ }, [
904
+ center,
905
+ zoom
906
+ ]);
907
+ useEffect5(() => {
908
+ const handler = debounce(() => {
909
+ onChange?.({
910
+ center: map.getCenter(),
911
+ zoom: map.getZoom()
912
+ });
913
+ }, 100);
914
+ map.on("move", handler);
915
+ map.on("zoom", handler);
916
+ return () => {
917
+ map.off("move", handler);
918
+ map.off("zoom", handler);
919
+ };
920
+ }, [
921
+ map,
922
+ onChange
923
+ ]);
924
+ useEffect5(() => {
925
+ if (markers.length > 0) {
926
+ const bounds = latLngBounds(markers.map((marker) => marker.location));
927
+ map.fitBounds(bounds);
928
+ } else {
929
+ map.setView(defaults.center, defaults.zoom);
930
+ }
931
+ }, [
932
+ markers
933
+ ]);
934
+ return /* @__PURE__ */ React4.createElement("div", {
935
+ ref,
936
+ className: "flex w-full h-full overflow-hidden bg-baseSurface"
937
+ }, /* @__PURE__ */ React4.createElement(TileLayer, {
938
+ className: "dark:filter dark:grayscale dark:invert",
939
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
940
+ }), markers.map(({ id, title, location: { lat, lng } }) => {
941
+ return /* @__PURE__ */ React4.createElement(Marker, {
942
+ key: id,
943
+ position: {
944
+ lat,
945
+ lng
946
+ }
947
+ }, title && /* @__PURE__ */ React4.createElement(Popup, null, title));
948
+ }));
949
+ });
950
+ var CustomControl2 = ({ position, children }) => {
951
+ const map = useMap();
952
+ useEffect5(() => {
953
+ const control = new Control({
954
+ position
955
+ });
956
+ control.onAdd = () => {
957
+ const container = DomUtil.create("div", mx2("!m-0", controlPositions[position]));
958
+ DomEvent.disableClickPropagation(container);
959
+ DomEvent.disableScrollPropagation(container);
960
+ const root = createRoot(container);
961
+ root.render(/* @__PURE__ */ React4.createElement(ThemeProvider, {
962
+ tx: defaultTx
963
+ }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, children)));
964
+ return container;
965
+ };
966
+ control.addTo(map);
967
+ return () => {
968
+ control.remove();
969
+ };
970
+ }, [
971
+ map,
972
+ position,
973
+ children
974
+ ]);
975
+ return null;
976
+ };
977
+ var Map = {
978
+ Root: MapRoot,
979
+ Canvas: MapCanvas,
980
+ Zoom: ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
981
+ position,
982
+ ...props
983
+ }, /* @__PURE__ */ React4.createElement(ZoomControls, {
984
+ onAction
985
+ })),
986
+ Action: ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
987
+ position,
988
+ ...props
989
+ }, /* @__PURE__ */ React4.createElement(ActionControls, {
990
+ onAction
991
+ }))
992
+ };
993
+ export {
994
+ ActionControls,
995
+ Globe,
996
+ GlobeContextProvider,
997
+ Map,
998
+ ZoomControls,
999
+ closestPoint,
1000
+ controlPositions,
1001
+ createLayers,
1002
+ geoCircle2 as geoCircle,
1003
+ geoInertiaDrag,
1004
+ geoLine,
1005
+ geoPoint,
1006
+ geoToPosition,
1007
+ getDistance,
1008
+ loadTopology,
1009
+ positionToRotation,
1010
+ renderLayers,
1011
+ restrictAxis,
1012
+ timer,
1013
+ useDrag,
1014
+ useGlobeContext,
1015
+ useGlobeZoomHandler,
1016
+ useMapZoomHandler,
1017
+ useSpinner,
1018
+ useTour
1019
+ };
1020
+ //# sourceMappingURL=index.mjs.map