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