@dxos/react-ui-geo 0.10.0 → 0.11.0

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 (57) hide show
  1. package/dist/lib/chunk-countries-10m.mjs +10 -0
  2. package/dist/lib/chunk-countries-10m.mjs.map +1 -0
  3. package/dist/lib/chunk-countries-110m.mjs +10 -0
  4. package/dist/lib/chunk-countries-110m.mjs.map +1 -0
  5. package/dist/lib/chunk-countries-50m.mjs +10 -0
  6. package/dist/lib/chunk-countries-50m.mjs.map +1 -0
  7. package/dist/lib/data.mjs +12 -0
  8. package/dist/lib/data.mjs.map +1 -0
  9. package/dist/lib/index.mjs +1415 -0
  10. package/dist/lib/index.mjs.map +1 -0
  11. package/dist/lib/translations.mjs +12 -0
  12. package/dist/lib/translations.mjs.map +1 -0
  13. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  14. package/dist/types/src/components/Map/Map.d.ts +1 -1
  15. package/dist/types/src/components/Toolbar/Controls.d.ts +0 -2
  16. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
  17. package/dist/types/src/components/Toolbar/control-positions.d.ts +3 -0
  18. package/dist/types/src/components/Toolbar/control-positions.d.ts.map +1 -0
  19. package/dist/types/src/components/Toolbar/index.d.ts +1 -0
  20. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -1
  21. package/dist/types/src/hooks/context.d.ts.map +1 -1
  22. package/dist/types/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +14 -17
  24. package/src/components/Toolbar/Controls.tsx +0 -8
  25. package/src/components/Toolbar/control-positions.ts +16 -0
  26. package/src/components/Toolbar/index.ts +2 -0
  27. package/dist/lib/browser/chunk-SC2FBYFU.mjs +0 -17
  28. package/dist/lib/browser/chunk-SC2FBYFU.mjs.map +0 -7
  29. package/dist/lib/browser/countries-10m-CWWDOKH7.mjs +0 -6
  30. package/dist/lib/browser/countries-10m-CWWDOKH7.mjs.map +0 -7
  31. package/dist/lib/browser/countries-110m-72QBAA5E.mjs +0 -6
  32. package/dist/lib/browser/countries-110m-72QBAA5E.mjs.map +0 -7
  33. package/dist/lib/browser/countries-50m-H7SL7KVF.mjs +0 -6
  34. package/dist/lib/browser/countries-50m-H7SL7KVF.mjs.map +0 -7
  35. package/dist/lib/browser/data.mjs +0 -7
  36. package/dist/lib/browser/data.mjs.map +0 -7
  37. package/dist/lib/browser/index.mjs +0 -1666
  38. package/dist/lib/browser/index.mjs.map +0 -7
  39. package/dist/lib/browser/meta.json +0 -1
  40. package/dist/lib/browser/translations.mjs +0 -19
  41. package/dist/lib/browser/translations.mjs.map +0 -7
  42. package/dist/lib/node-esm/chunk-VZENBYLJ.mjs +0 -19
  43. package/dist/lib/node-esm/chunk-VZENBYLJ.mjs.map +0 -7
  44. package/dist/lib/node-esm/countries-10m-DJZV66KG.mjs +0 -8
  45. package/dist/lib/node-esm/countries-10m-DJZV66KG.mjs.map +0 -7
  46. package/dist/lib/node-esm/countries-110m-H3WY6K4Q.mjs +0 -8
  47. package/dist/lib/node-esm/countries-110m-H3WY6K4Q.mjs.map +0 -7
  48. package/dist/lib/node-esm/countries-50m-ZY7Z3IWD.mjs +0 -8
  49. package/dist/lib/node-esm/countries-50m-ZY7Z3IWD.mjs.map +0 -7
  50. package/dist/lib/node-esm/data.mjs +0 -8
  51. package/dist/lib/node-esm/data.mjs.map +0 -7
  52. package/dist/lib/node-esm/index.mjs +0 -1667
  53. package/dist/lib/node-esm/index.mjs.map +0 -7
  54. package/dist/lib/node-esm/meta.json +0 -1
  55. package/dist/lib/node-esm/translations.mjs +0 -21
  56. package/dist/lib/node-esm/translations.mjs.map +0 -7
  57. /package/src/hooks/{context.tsx → context.ts} +0 -0
@@ -1,1666 +0,0 @@
1
- import {
2
- loadTopology
3
- } from "./chunk-SC2FBYFU.mjs";
4
-
5
- // src/components/Globe/Globe.tsx
6
- import { selection as d3Selection, easeLinear, easeSinOut, geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition } from "d3";
7
- import React2, { forwardRef, useCallback as useCallback3, useEffect as useEffect6, useId, useImperativeHandle, useMemo as useMemo2, useRef, useState as useState4 } from "react";
8
- import { useResizeDetector } from "react-resize-detector";
9
- import { useComposedRefs, useControlledState, useDynamicRef, useThemeContext } from "@dxos/react-ui";
10
- import { composable, composableProps } from "@dxos/react-ui";
11
- import { mx } from "@dxos/ui-theme";
12
-
13
- // src/hooks/context.tsx
14
- import { createContext, useContext } from "react";
15
- import { raise } from "@dxos/debug";
16
- var GlobeContext = /* @__PURE__ */ createContext(void 0);
17
- var useGlobeContext = () => {
18
- return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
19
- };
20
-
21
- // src/hooks/useDrag.ts
22
- import { select as select2 } from "d3";
23
- import { useEffect } from "react";
24
-
25
- // src/util/animation.ts
26
- import { geoDistance } from "d3";
27
- import versor from "versor";
28
- var flyDuration = (p1, p2, base, scale) => Math.max(base, geoDistance(p1, p2) * scale);
29
- var createRotationTween = (projection, setRotation, r1, r2) => {
30
- const iv = versor.interpolate(r1, r2);
31
- return (t) => {
32
- projection.rotate(iv(t));
33
- setRotation(projection.rotate());
34
- };
35
- };
36
-
37
- // src/util/debug.ts
38
- var debug = false;
39
- var timer = (cb) => {
40
- const start = Date.now();
41
- const data = cb();
42
- const t = Date.now() - start / 1e3;
43
- if (debug) {
44
- console.log({
45
- t,
46
- data
47
- });
48
- }
49
- return data;
50
- };
51
-
52
- // src/util/inertia.ts
53
- import { drag, select, timer as timer2 } from "d3";
54
- import versor2 from "versor";
55
- var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
56
- var geoInertiaDrag = (target, render, projection, options) => {
57
- if (!options) {
58
- options = {};
59
- }
60
- if (target.node) {
61
- target = target.node();
62
- }
63
- target = select(target);
64
- const linear = (options.mode ?? "linear") === "linear";
65
- const axis = restrictAxis(options.lockTilt ? [
66
- true,
67
- false,
68
- false
69
- ] : [
70
- true,
71
- true,
72
- true
73
- ]);
74
- const sharedHandlers = {
75
- projection,
76
- render: (rotation) => {
77
- projection.rotate(rotation);
78
- render && render();
79
- },
80
- axis,
81
- start: options.start,
82
- move: options.move,
83
- end: options.end,
84
- stop: options.stop,
85
- finish: options.finish,
86
- time: options.time,
87
- hold: options.hold
88
- };
89
- const inertia = linear ? geoInertiaDragLinearHelper({
90
- ...sharedHandlers,
91
- sensitivity: options.sensitivity,
92
- getZoom: options.getZoom
93
- }) : geoInertiaDragHelper(sharedHandlers);
94
- target.call(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 = versor2.cartesian(projection.invert(inertia.position));
109
- r0 = projection.rotate();
110
- q0 = versor2(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 = versor2.cartesian(inv);
119
- const q1 = versor2.multiply(q0, versor2.delta(v0, v1));
120
- const r1 = versor2.rotation(q1);
121
- const r2 = opt.axis(r0, r1);
122
- opt.render(r2);
123
- opt.move && opt.move();
124
- },
125
- end: () => {
126
- v10 = versor2.cartesian(projection.invert(inertia.position.map((d, i) => d - inertia.velocity[i] / 1e3)));
127
- q10 = versor2(projection.rotate());
128
- v11 = versor2.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 = versor2.rotation(versor2.multiply(q10, versor2.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
- var DEFAULT_LINEAR_SENSITIVITY = 0.25;
143
- var geoInertiaDragLinearHelper = (opt) => {
144
- const projection = opt.projection;
145
- const sensitivity = opt.sensitivity ?? DEFAULT_LINEAR_SENSITIVITY;
146
- const gain = () => sensitivity / Math.max(opt.getZoom?.() ?? 1, 0.1);
147
- let r0;
148
- let p0;
149
- let kStart;
150
- let rEnd;
151
- let vEnd;
152
- const inertia = inertiaHelper({
153
- axis: opt.axis,
154
- start: () => {
155
- r0 = projection.rotate();
156
- p0 = [
157
- inertia.position[0],
158
- inertia.position[1]
159
- ];
160
- kStart = gain();
161
- opt.start && opt.start();
162
- },
163
- move: () => {
164
- const dx = inertia.position[0] - p0[0];
165
- const dy = inertia.position[1] - p0[1];
166
- const r1 = [
167
- r0[0] + dx * kStart,
168
- r0[1] - dy * kStart,
169
- 0
170
- ];
171
- const r2 = opt.axis(r0, r1);
172
- opt.render(r2);
173
- opt.move && opt.move();
174
- },
175
- end: () => {
176
- rEnd = projection.rotate();
177
- vEnd = [
178
- inertia.velocity[0],
179
- inertia.velocity[1]
180
- ];
181
- opt.end && opt.end();
182
- },
183
- stop: opt.stop,
184
- finish: opt.finish,
185
- render: (t) => {
186
- const r1 = [
187
- rEnd[0] + vEnd[0] * kStart * t,
188
- rEnd[1] - vEnd[1] * kStart * t,
189
- 0
190
- ];
191
- const r2 = opt.axis(rEnd, r1);
192
- opt.render && opt.render(r2);
193
- },
194
- time: opt.time
195
- });
196
- return inertia;
197
- };
198
- function inertiaHelper(opt) {
199
- const A = opt.time || 5e3;
200
- const limit = 1.0001;
201
- const B = -Math.log(1 - 1 / limit);
202
- const inertia = {
203
- position: [
204
- 0,
205
- 0
206
- ],
207
- velocity: [
208
- 0,
209
- 0
210
- ],
211
- timer: timer2(() => {
212
- }),
213
- time: 0,
214
- t: 0,
215
- start: function(ev) {
216
- const position = [
217
- ev.x,
218
- ev.y
219
- ];
220
- inertia.position = position;
221
- inertia.velocity = [
222
- 0,
223
- 0
224
- ];
225
- inertia.timer.stop();
226
- this.classList.remove("inertia");
227
- this.classList.add("dragging");
228
- opt.start && opt.start.call(this, position);
229
- },
230
- move: function(ev) {
231
- const position = [
232
- ev.x,
233
- ev.y
234
- ];
235
- const time = performance.now();
236
- const deltaTime = time - inertia.time;
237
- const decay = 1 - Math.exp(-deltaTime / 1e3);
238
- inertia.velocity = inertia.velocity.map((d, i) => {
239
- const deltaPos = position[i] - inertia.position[i];
240
- const deltaTime2 = time - inertia.time;
241
- return 1e3 * (1 - decay) * deltaPos / deltaTime2 + d * decay;
242
- });
243
- inertia.velocity = opt.axis([
244
- 0,
245
- 0
246
- ], inertia.velocity);
247
- inertia.time = time;
248
- inertia.position = position;
249
- opt.move && opt.move.call(this, position);
250
- },
251
- end: function(ev) {
252
- this.classList.remove("dragging", "inertia");
253
- const v = inertia.velocity;
254
- if (v[0] * v[0] + v[1] * v[1] < 100) {
255
- inertia.timer.stop();
256
- return opt.stop && opt.stop();
257
- }
258
- const time = performance.now();
259
- const deltaTime = time - inertia.time;
260
- if (opt.hold === void 0) {
261
- opt.hold = 100;
262
- }
263
- if (deltaTime >= opt.hold) {
264
- inertia.timer.stop();
265
- return opt.stop && opt.stop();
266
- }
267
- this.classList.add("inertia");
268
- opt.end && opt.end();
269
- const self = this;
270
- inertia.timer.restart((e) => {
271
- inertia.t = limit * (1 - Math.exp(-B * e / A));
272
- opt.render && opt.render(inertia.t);
273
- if (inertia.t > 1) {
274
- inertia.timer.stop();
275
- self.classList.remove("inertia");
276
- inertia.velocity = [
277
- 0,
278
- 0
279
- ];
280
- inertia.t = 1;
281
- opt.finish && opt.finish();
282
- }
283
- });
284
- }
285
- };
286
- inertia.timer.stop();
287
- return inertia;
288
- }
289
-
290
- // src/util/path.ts
291
- import { geoCircle as d3GeoCircle } from "d3";
292
- var positionToRotation = ([lng, lat], tilt = 0) => [
293
- -lng,
294
- tilt - lat,
295
- 0
296
- ];
297
- var geoToPosition = ({ lat, lng }) => [
298
- lng,
299
- lat
300
- ];
301
- var geoPoint = (point2) => ({
302
- type: "Point",
303
- coordinates: geoToPosition(point2)
304
- });
305
- var geoCircle = ({ lat, lng }, radius) => d3GeoCircle().radius(radius).center([
306
- lng,
307
- lat
308
- ])();
309
- var geoLine = (p1, p2) => ({
310
- type: "LineString",
311
- coordinates: [
312
- [
313
- p1.lng,
314
- p1.lat
315
- ],
316
- [
317
- p2.lng,
318
- p2.lat
319
- ]
320
- ]
321
- });
322
- var closestPoint = (points, target) => {
323
- if (points.length === 0) {
324
- return target;
325
- }
326
- let closestPoint2 = points[0];
327
- let minDistance = getDistance(points[0], target);
328
- for (const point2 of points) {
329
- const distance = getDistance(point2, target);
330
- if (distance < minDistance) {
331
- minDistance = distance;
332
- closestPoint2 = point2;
333
- }
334
- }
335
- return closestPoint2;
336
- };
337
- var getDistance = (point1, point2) => {
338
- const dx = point1[0] - point2[0];
339
- const dy = point1[1] - point2[1];
340
- return Math.sqrt(dx * dx + dy * dy);
341
- };
342
-
343
- // src/util/render.ts
344
- import { geoBounds, geoCentroid, geoDistance as geoDistance2, geoGraticule } from "d3";
345
- import { feature, mesh } from "topojson-client";
346
- var RAD_TO_DEG = 180 / Math.PI;
347
- var computeBounds = (geometry) => {
348
- const feat = {
349
- type: "Feature",
350
- geometry,
351
- properties: {}
352
- };
353
- const centroid = geoCentroid(feat);
354
- const [[w, s], [e, n]] = geoBounds(feat);
355
- const corners = [
356
- [
357
- w,
358
- s
359
- ],
360
- [
361
- w,
362
- n
363
- ],
364
- [
365
- e,
366
- s
367
- ],
368
- [
369
- e,
370
- n
371
- ]
372
- ];
373
- let radius = 0;
374
- for (const corner of corners) {
375
- const d = geoDistance2(centroid, corner) * RAD_TO_DEG;
376
- if (d > radius) {
377
- radius = d;
378
- }
379
- }
380
- return {
381
- geometry,
382
- centroid,
383
- radius
384
- };
385
- };
386
- var createLayers = (topology, features, styles) => {
387
- const layers = [];
388
- if (styles.water) {
389
- layers.push({
390
- styles: styles.water,
391
- path: {
392
- type: "Sphere"
393
- }
394
- });
395
- }
396
- if (styles.graticule) {
397
- layers.push({
398
- styles: styles.graticule,
399
- path: geoGraticule().step([
400
- 6,
401
- 6
402
- ])()
403
- });
404
- }
405
- if (topology) {
406
- if (styles.land) {
407
- if (topology.objects.countries) {
408
- const fc = feature(topology, topology.objects.countries);
409
- const memberGeoms = fc.features.map((f) => f.geometry);
410
- const bounds = memberGeoms.map(computeBounds);
411
- layers.push({
412
- styles: styles.land,
413
- path: {
414
- type: "GeometryCollection",
415
- geometries: memberGeoms
416
- },
417
- cullable: bounds
418
- });
419
- } else if (topology.objects.land) {
420
- layers.push({
421
- styles: styles.land,
422
- path: feature(topology, topology.objects.land)
423
- });
424
- }
425
- }
426
- if (topology.objects.countries && styles.border) {
427
- layers.push({
428
- styles: styles.border,
429
- path: mesh(topology, topology.objects.countries, (a, b) => a !== b)
430
- });
431
- }
432
- if (topology.objects.dots && styles.dots) {
433
- layers.push({
434
- styles: styles.dots,
435
- path: topology.objects.dots
436
- });
437
- }
438
- }
439
- if (features) {
440
- const { points, lines } = features;
441
- if (lines && styles.line) {
442
- layers.push({
443
- styles: styles.line,
444
- path: {
445
- type: "GeometryCollection",
446
- geometries: lines.map(({ source, target }) => geoLine(source, target))
447
- }
448
- });
449
- }
450
- if (points && styles.point) {
451
- layers.push({
452
- styles: styles.point,
453
- path: {
454
- type: "GeometryCollection",
455
- geometries: points.map((point2) => geoPoint(point2))
456
- }
457
- });
458
- }
459
- }
460
- return layers;
461
- };
462
- var renderLayers = (generator, layers = [], scale, styles, viewCenter) => {
463
- const context = generator.context();
464
- const { canvas: { width, height } } = context;
465
- context.reset();
466
- if (styles.background) {
467
- context.fillStyle = styles.background.fillStyle;
468
- context.fillRect(0, 0, width, height);
469
- } else {
470
- context.clearRect(0, 0, width, height);
471
- }
472
- layers.forEach((layer) => {
473
- const { path, styles: styles2, cullable } = layer;
474
- context.save();
475
- let fill = false;
476
- let stroke = false;
477
- if (styles2) {
478
- Object.entries(styles2).forEach(([key, value]) => {
479
- if (key === "pointRadius") {
480
- generator.pointRadius(value * scale);
481
- } else {
482
- context[key] = value;
483
- fill ||= key === "fillStyle";
484
- stroke ||= key === "strokeStyle";
485
- }
486
- });
487
- }
488
- let renderPath = path;
489
- if (cullable && viewCenter) {
490
- const geometries = [];
491
- for (let index = 0; index < cullable.length; index++) {
492
- const bounds = cullable[index];
493
- const angularDistance = geoDistance2(viewCenter, bounds.centroid) * RAD_TO_DEG;
494
- if (angularDistance < 90 + bounds.radius) {
495
- geometries.push(bounds.geometry);
496
- }
497
- }
498
- renderPath = {
499
- type: "GeometryCollection",
500
- geometries
501
- };
502
- }
503
- context.beginPath();
504
- generator(renderPath);
505
- fill && context.fill();
506
- stroke && context.stroke();
507
- context.restore();
508
- });
509
- return context;
510
- };
511
-
512
- // src/util/styles.ts
513
- var POINT_COLOR = "rgb(220, 38, 38)";
514
- var LINE_COLOR = "rgba(220, 38, 38, 0.5)";
515
- var globeStyles = (themeMode) => themeMode === "dark" ? {
516
- water: {
517
- fillStyle: "#191919"
518
- },
519
- land: {
520
- fillStyle: "#444",
521
- strokeStyle: "#222"
522
- },
523
- border: {
524
- strokeStyle: "#111"
525
- },
526
- graticule: {
527
- strokeStyle: "#111"
528
- },
529
- line: {
530
- lineWidth: 1.5,
531
- lineDash: [
532
- 4,
533
- 16
534
- ],
535
- strokeStyle: LINE_COLOR
536
- },
537
- point: {
538
- radius: 0.2,
539
- fillStyle: POINT_COLOR
540
- }
541
- } : {
542
- water: {
543
- fillStyle: "#C0DAE4"
544
- },
545
- land: {
546
- fillStyle: "#C2D8B4",
547
- strokeStyle: "#A6C291"
548
- },
549
- line: {
550
- lineWidth: 1.5,
551
- lineDash: [
552
- 4,
553
- 16
554
- ],
555
- strokeStyle: LINE_COLOR
556
- },
557
- point: {
558
- radius: 0.2,
559
- fillStyle: POINT_COLOR
560
- }
561
- };
562
-
563
- // src/hooks/useDrag.ts
564
- var useDrag = (controller, options = {}) => {
565
- useEffect(() => {
566
- const canvas = controller?.canvas;
567
- if (!canvas || options.disabled) {
568
- return;
569
- }
570
- const inertia = geoInertiaDrag(select2(canvas), () => {
571
- controller.setRotation(controller.projection.rotate());
572
- options.onUpdate?.({
573
- type: "move",
574
- controller
575
- });
576
- }, controller.projection, {
577
- lockTilt: options.lockTilt,
578
- mode: options.mode,
579
- sensitivity: options.sensitivity,
580
- // Zoom-driven gain: matches useWheel — degrees-per-pixel shrinks as the
581
- // globe gets larger on screen so the drag feel is consistent across zoom.
582
- getZoom: () => controller.zoom,
583
- time: 3e3,
584
- start: () => options.onUpdate?.({
585
- type: "start",
586
- controller
587
- }),
588
- finish: () => options.onUpdate?.({
589
- type: "end",
590
- controller
591
- })
592
- });
593
- return () => {
594
- cancelDrag(select2(canvas));
595
- inertia?.timer?.stop();
596
- };
597
- }, [
598
- controller,
599
- JSON.stringify(options)
600
- ]);
601
- };
602
- var cancelDrag = (node) => node.on(".drag", null);
603
-
604
- // src/hooks/useGlobeZoomHandler.ts
605
- import { useCallback } from "react";
606
- var ZOOM_FACTOR = 0.1;
607
- var useGlobeZoomHandler = (controller) => {
608
- return useCallback((event) => {
609
- if (!controller) {
610
- return;
611
- }
612
- switch (event) {
613
- case "zoom-in": {
614
- controller.setZoom((zoom) => {
615
- return zoom * (1 + ZOOM_FACTOR);
616
- });
617
- break;
618
- }
619
- case "zoom-out": {
620
- controller.setZoom((zoom) => {
621
- return zoom * (1 - ZOOM_FACTOR);
622
- });
623
- break;
624
- }
625
- }
626
- }, [
627
- controller
628
- ]);
629
- };
630
-
631
- // src/hooks/useMapZoomHandler.ts
632
- import { useCallback as useCallback2 } from "react";
633
- var useMapZoomHandler = (controller) => {
634
- return useCallback2((event) => {
635
- if (!controller) {
636
- return;
637
- }
638
- switch (event) {
639
- case "zoom-in": {
640
- controller.setZoom((scale) => scale + 1);
641
- break;
642
- }
643
- case "zoom-out": {
644
- controller.setZoom((scale) => scale - 1);
645
- break;
646
- }
647
- }
648
- }, [
649
- controller
650
- ]);
651
- };
652
-
653
- // src/hooks/useSimplifiedTopology.ts
654
- import { useMemo } from "react";
655
- import { presimplify, quantile, simplify } from "topojson-simplify";
656
- var DEFAULT_TIERS = [
657
- {
658
- minZoom: 0,
659
- percentile: 0.95
660
- },
661
- {
662
- minZoom: 2,
663
- percentile: 0.85
664
- },
665
- {
666
- minZoom: 4,
667
- percentile: 0.6
668
- },
669
- {
670
- minZoom: 7,
671
- percentile: 0.3
672
- },
673
- {
674
- minZoom: 12,
675
- percentile: 0
676
- }
677
- ];
678
- var pickTier = (zoom, tiers) => {
679
- let match = tiers[0];
680
- for (const tier of tiers) {
681
- if (zoom >= tier.minZoom) {
682
- match = tier;
683
- }
684
- }
685
- return match;
686
- };
687
- var useSimplifiedTopology = (topology, zoom, options = {}) => {
688
- const { tiers = DEFAULT_TIERS } = options;
689
- const presimplified = useMemo(() => topology ? presimplify(topology) : void 0, [
690
- topology
691
- ]);
692
- const tier = pickTier(zoom, tiers);
693
- return useMemo(() => {
694
- if (!presimplified) {
695
- return void 0;
696
- }
697
- if (tier.percentile <= 0) {
698
- return presimplified;
699
- }
700
- const minWeight = quantile(presimplified, tier.percentile);
701
- return simplify(presimplified, minWeight);
702
- }, [
703
- presimplified,
704
- tier
705
- ]);
706
- };
707
-
708
- // src/hooks/useSpinner.ts
709
- import { timer as d3Timer } from "d3";
710
- import { useEffect as useEffect2, useState } from "react";
711
- var useSpinner = (controller, options = {}) => {
712
- const [running, setRunning] = useState(false);
713
- useEffect2(() => {
714
- let timer3;
715
- const start = () => {
716
- const delta = options.delta ?? [
717
- 1e-3,
718
- 0,
719
- 0
720
- ];
721
- let t = 0;
722
- let lastRotation = controller.projection.rotate();
723
- timer3 = d3Timer((elapsed) => {
724
- const dt = elapsed - t;
725
- t = elapsed;
726
- const rotation = [
727
- lastRotation[0] + delta[0] * dt,
728
- lastRotation[1] + delta[1] * dt,
729
- lastRotation[2] + delta[2] * dt
730
- ];
731
- lastRotation = rotation;
732
- controller.setRotation(rotation);
733
- });
734
- };
735
- const stop = () => {
736
- if (timer3) {
737
- timer3.stop();
738
- timer3 = void 0;
739
- }
740
- };
741
- if (controller && running) {
742
- start();
743
- } else {
744
- stop();
745
- }
746
- return () => stop();
747
- }, [
748
- controller,
749
- running
750
- ]);
751
- return [
752
- () => {
753
- if (!options.disabled) {
754
- setRunning(true);
755
- }
756
- },
757
- () => setRunning(false)
758
- ];
759
- };
760
-
761
- // src/hooks/useTopology.ts
762
- import { useEffect as useEffect3, useState as useState2 } from "react";
763
- import { log } from "@dxos/log";
764
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-geo/src/hooks/useTopology.ts";
765
- var DEFAULT_TIERS2 = [
766
- {
767
- minZoom: 0,
768
- level: "110m"
769
- }
770
- ];
771
- var pickTier2 = (zoom, tiers) => {
772
- let match = tiers[0];
773
- for (const tier of tiers) {
774
- if (zoom >= tier.minZoom) {
775
- match = tier;
776
- }
777
- }
778
- return match;
779
- };
780
- var topologyCache = /* @__PURE__ */ new Map();
781
- var useTopology = (zoom, options = {}) => {
782
- const { tiers = DEFAULT_TIERS2 } = options;
783
- const level = zoom === void 0 ? "110m" : pickTier2(zoom, tiers).level;
784
- const [topology, setTopology] = useState2(() => topologyCache.get(level));
785
- useEffect3(() => {
786
- const cached = topologyCache.get(level);
787
- if (cached) {
788
- setTopology(cached);
789
- return;
790
- }
791
- let disposed = false;
792
- void loadTopology(level).then((loaded) => {
793
- topologyCache.set(level, loaded);
794
- if (!disposed) {
795
- setTopology(loaded);
796
- }
797
- }).catch((err) => {
798
- if (!disposed) {
799
- log.warn("failed to load topology", {
800
- level,
801
- err
802
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 52, S: void 0 });
803
- }
804
- });
805
- return () => {
806
- disposed = true;
807
- };
808
- }, [
809
- level
810
- ]);
811
- return topology;
812
- };
813
-
814
- // src/hooks/useTour.ts
815
- import { geoInterpolate, geoPath } from "d3";
816
- import { useEffect as useEffect4, useState as useState3 } from "react";
817
- var defaultDuration = 1500;
818
- var useTour = (controller, points, options = {}) => {
819
- const [running, setRunning] = useState3(options.running ?? false);
820
- useEffect4(() => {
821
- if (!controller || !running) {
822
- return;
823
- }
824
- let cancelled = false;
825
- const t = setTimeout(async () => {
826
- const { canvas, projection } = controller;
827
- const context = canvas.getContext("2d", {
828
- alpha: false
829
- });
830
- const path = geoPath(projection, context).pointRadius(2);
831
- try {
832
- const tourPoints = [
833
- ...points
834
- ];
835
- if (options.loop) {
836
- tourPoints.push(tourPoints[0]);
837
- }
838
- let last;
839
- for (const next of tourPoints) {
840
- if (cancelled) {
841
- break;
842
- }
843
- const p1 = last ? geoToPosition(last) : void 0;
844
- const p2 = geoToPosition(next);
845
- const ip = geoInterpolate(p1 ?? p2, p2);
846
- const onTick = (t2) => {
847
- const t1 = Math.max(0, Math.min(1, t2 * 2 - 1));
848
- const t22 = Math.min(1, t2 * 2);
849
- context.save();
850
- try {
851
- context.beginPath();
852
- context.strokeStyle = options.styles?.arc?.strokeStyle ?? "yellow";
853
- context.lineWidth = (options.styles?.arc?.lineWidth ?? 1.5) * (controller.zoom ?? 1);
854
- context.setLineDash(options.styles?.arc?.lineDash ?? []);
855
- path({
856
- type: "LineString",
857
- coordinates: [
858
- ip(t1),
859
- ip(t22)
860
- ]
861
- });
862
- context.stroke();
863
- context.beginPath();
864
- context.fillStyle = options.styles?.cursor?.fillStyle ?? "orange";
865
- path.pointRadius((options.styles?.cursor?.pointRadius ?? 2) * (controller.zoom ?? 1));
866
- path({
867
- type: "Point",
868
- coordinates: ip(t22)
869
- });
870
- context.fill();
871
- } finally {
872
- context.restore();
873
- }
874
- };
875
- await controller.flyTo(next, {
876
- duration: options.duration ?? defaultDuration,
877
- tilt: options.tilt ?? 0,
878
- onTick
879
- });
880
- last = next;
881
- }
882
- } catch {
883
- } finally {
884
- if (!cancelled) {
885
- setRunning(false);
886
- }
887
- }
888
- });
889
- return () => {
890
- cancelled = true;
891
- clearTimeout(t);
892
- controller.cancelFlyTo();
893
- };
894
- }, [
895
- controller,
896
- running,
897
- JSON.stringify(options)
898
- ]);
899
- return [
900
- running,
901
- setRunning
902
- ];
903
- };
904
-
905
- // src/hooks/useWheel.ts
906
- import { useEffect as useEffect5 } from "react";
907
- var DEFAULT_SENSITIVITY = 0.25;
908
- var DEFAULT_ZOOM_SENSITIVITY = 0.01;
909
- var useWheel = (controller, options = {}) => {
910
- useEffect5(() => {
911
- const canvas = controller?.canvas;
912
- if (!canvas || options.disabled) {
913
- return;
914
- }
915
- const sensitivity = options.sensitivity ?? DEFAULT_SENSITIVITY;
916
- const zoomSensitivity = options.zoomSensitivity ?? DEFAULT_ZOOM_SENSITIVITY;
917
- const handleWheel = (event) => {
918
- event.preventDefault();
919
- if (event.ctrlKey) {
920
- const factor = Math.exp(-event.deltaY * zoomSensitivity);
921
- controller.setZoom(controller.zoom * factor);
922
- } else {
923
- const [lambda, phi, gamma] = controller.projection.rotate();
924
- const k = sensitivity / Math.max(controller.zoom, 0.1);
925
- const next = [
926
- lambda - event.deltaX * k,
927
- phi + event.deltaY * k,
928
- gamma
929
- ];
930
- controller.projection.rotate(next);
931
- controller.setRotation(controller.projection.rotate());
932
- }
933
- options.onUpdate?.(controller);
934
- };
935
- canvas.addEventListener("wheel", handleWheel, {
936
- passive: false
937
- });
938
- return () => {
939
- canvas.removeEventListener("wheel", handleWheel);
940
- };
941
- }, [
942
- controller,
943
- options.disabled,
944
- options.sensitivity,
945
- options.zoomSensitivity,
946
- options.onUpdate
947
- ]);
948
- };
949
-
950
- // src/components/Toolbar/Controls.tsx
951
- import React from "react";
952
- import { IconButton, Toolbar, useTranslation } from "@dxos/react-ui";
953
- import { translationKey } from "#translations";
954
- var controlPositions = {
955
- topleft: "top-2 left-2",
956
- topright: "top-2 right-2",
957
- bottomleft: "bottom-2 left-2",
958
- bottomright: "bottom-2 right-2"
959
- };
960
- var ZoomControls = ({ classNames, onAction }) => {
961
- const { t } = useTranslation(translationKey);
962
- return /* @__PURE__ */ React.createElement(Toolbar.Root, {
963
- classNames: [
964
- "gap-2",
965
- classNames
966
- ]
967
- }, /* @__PURE__ */ React.createElement(IconButton, {
968
- icon: "ph--plus--regular",
969
- iconOnly: true,
970
- label: t("zoom-in-icon.button"),
971
- onClick: () => onAction?.("zoom-in")
972
- }), /* @__PURE__ */ React.createElement(IconButton, {
973
- icon: "ph--minus--regular",
974
- iconOnly: true,
975
- label: t("zoom-out-icon.button"),
976
- onClick: () => onAction?.("zoom-out")
977
- }));
978
- };
979
- var ActionControls = ({ classNames, onAction }) => {
980
- const { t } = useTranslation(translationKey);
981
- return /* @__PURE__ */ React.createElement(Toolbar.Root, {
982
- classNames: [
983
- "gap-2",
984
- classNames
985
- ]
986
- }, /* @__PURE__ */ React.createElement(IconButton, {
987
- icon: "ph--path--regular",
988
- iconOnly: true,
989
- label: t("start-icon.button"),
990
- onClick: () => onAction?.("start")
991
- }), /* @__PURE__ */ React.createElement(IconButton, {
992
- icon: "ph--globe-hemisphere-west--regular",
993
- iconOnly: true,
994
- label: t("toggle-icon.button"),
995
- onClick: () => onAction?.("toggle")
996
- }));
997
- };
998
-
999
- // src/components/Globe/Globe.tsx
1000
- var defaultStyles = {
1001
- light: {
1002
- background: {
1003
- fillStyle: "#EEE"
1004
- },
1005
- water: {
1006
- fillStyle: "#555"
1007
- },
1008
- land: {
1009
- fillStyle: "#999"
1010
- },
1011
- line: {
1012
- strokeStyle: "darkred"
1013
- },
1014
- point: {
1015
- fillStyle: "#111111",
1016
- strokeStyle: "#111111",
1017
- strokeWidth: 1,
1018
- pointRadius: 0.5
1019
- }
1020
- },
1021
- dark: {
1022
- background: {
1023
- fillStyle: "#111111"
1024
- },
1025
- water: {
1026
- fillStyle: "#123E6A"
1027
- },
1028
- land: {
1029
- fillStyle: "#032153"
1030
- },
1031
- line: {
1032
- strokeStyle: "#111111"
1033
- },
1034
- point: {
1035
- fillStyle: "#111111",
1036
- strokeStyle: "#111111",
1037
- strokeWidth: 1,
1038
- pointRadius: 0.5
1039
- }
1040
- }
1041
- };
1042
- var projectionMap = {
1043
- "orthographic": geoOrthographic,
1044
- "mercator": geoMercator,
1045
- "transverse-mercator": geoTransverseMercator
1046
- };
1047
- var getProjection = (type = "orthographic") => {
1048
- if (typeof type === "string") {
1049
- const constructor = projectionMap[type] ?? geoOrthographic;
1050
- return constructor();
1051
- }
1052
- return type ?? geoOrthographic();
1053
- };
1054
- var DEFAULT_ZOOM = 1.5;
1055
- var GlobeRoot = /* @__PURE__ */ forwardRef(({ children, center: centerProp, zoom: zoomProp = DEFAULT_ZOOM, translation: translationProp, rotation: rotationProp }, forwardedRef) => {
1056
- const [size, setSize] = useState4({
1057
- width: 0,
1058
- height: 0
1059
- });
1060
- const [center, setCenter] = useControlledState(centerProp);
1061
- const [zoom, setZoom] = useControlledState(zoomProp);
1062
- const [translation, setTranslation] = useControlledState(translationProp);
1063
- const [rotation, setRotation] = useControlledState(rotationProp);
1064
- const [controller, setController] = useState4(null);
1065
- const registerController = useCallback3((next) => setController(next), []);
1066
- useImperativeHandle(forwardedRef, () => controller, [
1067
- controller
1068
- ]);
1069
- return /* @__PURE__ */ React2.createElement(GlobeContext.Provider, {
1070
- value: {
1071
- size,
1072
- center,
1073
- zoom,
1074
- translation,
1075
- rotation,
1076
- setSize,
1077
- setCenter,
1078
- setZoom,
1079
- setTranslation,
1080
- setRotation,
1081
- registerController
1082
- }
1083
- }, children);
1084
- });
1085
- GlobeRoot.displayName = "Globe.Root";
1086
- var GlobeViewport = composable(({ children, ...props }, forwardedRef) => {
1087
- const { setSize } = useGlobeContext();
1088
- const localRef = useRef(null);
1089
- const composedRef = useComposedRefs(localRef, forwardedRef);
1090
- const { width, height } = useResizeDetector({
1091
- targetRef: localRef
1092
- });
1093
- useEffect6(() => {
1094
- setSize({
1095
- width: width ?? 0,
1096
- height: height ?? 0
1097
- });
1098
- }, [
1099
- width,
1100
- height,
1101
- setSize
1102
- ]);
1103
- return /* @__PURE__ */ React2.createElement("div", {
1104
- ...composableProps(props, {
1105
- classNames: "relative dx-container"
1106
- }),
1107
- ref: composedRef
1108
- }, children);
1109
- });
1110
- GlobeViewport.displayName = "Globe.Viewport";
1111
- var GlobeCanvas = ({ projection: projectionProp, topology, features, styles: stylesProp }) => {
1112
- const { themeMode } = useThemeContext();
1113
- const styles = useMemo2(() => stylesProp ?? defaultStyles[themeMode], [
1114
- stylesProp,
1115
- themeMode
1116
- ]);
1117
- const { size, center, zoom, translation, rotation, setZoom, setTranslation, setRotation, registerController } = useGlobeContext();
1118
- const zoomRef = useDynamicRef(zoom);
1119
- const [canvas, setCanvas] = useState4(null);
1120
- const canvasRef = (canvas2) => setCanvas(canvas2);
1121
- const projection = useMemo2(() => getProjection(projectionProp), [
1122
- projectionProp
1123
- ]);
1124
- const layers = useMemo2(() => {
1125
- return timer(() => createLayers(topology, features, styles));
1126
- }, [
1127
- topology,
1128
- features,
1129
- styles
1130
- ]);
1131
- useEffect6(() => {
1132
- if (center) {
1133
- setRotation(positionToRotation(geoToPosition(center)));
1134
- }
1135
- }, [
1136
- center
1137
- ]);
1138
- const flyToSelection = useMemo2(() => d3Selection(), []);
1139
- const flyToTransitionName = `globe-fly-to-${useId()}`;
1140
- useEffect6(() => () => {
1141
- flyToSelection.interrupt(flyToTransitionName);
1142
- }, [
1143
- flyToSelection,
1144
- flyToTransitionName
1145
- ]);
1146
- const zooming = useRef(false);
1147
- const controller = useMemo2(() => {
1148
- return {
1149
- canvas,
1150
- projection,
1151
- get zoom() {
1152
- return zoomRef.current;
1153
- },
1154
- translation,
1155
- rotation,
1156
- setZoom: (state) => {
1157
- if (typeof state === "function") {
1158
- const is = interpolateNumber(zoomRef.current, state(zoomRef.current));
1159
- transition().ease(zooming.current ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => setZoom(is(t))).on("end", () => {
1160
- zooming.current = false;
1161
- });
1162
- } else {
1163
- setZoom(state);
1164
- }
1165
- },
1166
- setTranslation,
1167
- setRotation,
1168
- flyTo: (target, options = {}) => {
1169
- const { duration = 1200, tilt = 0, onTick } = options;
1170
- const p2 = geoToPosition(target);
1171
- const r1 = projection.rotate();
1172
- const r2 = positionToRotation(p2, tilt);
1173
- const p1 = [
1174
- -r1[0],
1175
- -r1[1]
1176
- ];
1177
- const rotationTween = createRotationTween(projection, setRotation, r1, r2);
1178
- const iz = target.zoom !== void 0 ? interpolateNumber(zoomRef.current, target.zoom) : void 0;
1179
- flyToSelection.interrupt(flyToTransitionName);
1180
- const tx = flyToSelection.transition(flyToTransitionName).duration(flyDuration(p1, p2, duration, 1500));
1181
- if (onTick) {
1182
- tx.tween("flyToOnTick", () => onTick);
1183
- }
1184
- tx.tween("flyToRotation", () => rotationTween);
1185
- if (iz) {
1186
- tx.tween("flyToZoom", () => (t) => setZoom(iz(t)));
1187
- }
1188
- return tx.end();
1189
- },
1190
- cancelFlyTo: () => {
1191
- flyToSelection.interrupt(flyToTransitionName);
1192
- }
1193
- };
1194
- }, [
1195
- canvas,
1196
- projection,
1197
- flyToSelection,
1198
- flyToTransitionName
1199
- ]);
1200
- useEffect6(() => {
1201
- registerController(controller);
1202
- return () => registerController(null);
1203
- }, [
1204
- registerController,
1205
- controller
1206
- ]);
1207
- const generator = useMemo2(() => canvas && projection && geoPath2(projection, canvas.getContext("2d")), [
1208
- canvas,
1209
- projection
1210
- ]);
1211
- useEffect6(() => {
1212
- if (canvas && projection) {
1213
- timer(() => {
1214
- projection.scale(Math.min(size.width, size.height) / 2 * zoom).translate([
1215
- size.width / 2 + (translation?.x ?? 0),
1216
- size.height / 2 + (translation?.y ?? 0)
1217
- ]).rotate(rotation ?? [
1218
- 0,
1219
- 0,
1220
- 0
1221
- ]);
1222
- const isOrthographic = !projectionProp || projectionProp === "orthographic";
1223
- const [lambda, phi] = rotation ?? [
1224
- 0,
1225
- 0,
1226
- 0
1227
- ];
1228
- const viewCenter = isOrthographic ? [
1229
- -lambda,
1230
- -phi
1231
- ] : void 0;
1232
- renderLayers(generator, layers, zoom, styles, viewCenter);
1233
- });
1234
- }
1235
- }, [
1236
- generator,
1237
- size,
1238
- zoom,
1239
- translation,
1240
- rotation,
1241
- layers,
1242
- projectionProp
1243
- ]);
1244
- if (!size.width || !size.height) {
1245
- return null;
1246
- }
1247
- return /* @__PURE__ */ React2.createElement("canvas", {
1248
- ref: canvasRef,
1249
- className: "bg-base-surface",
1250
- width: size.width,
1251
- height: size.height
1252
- });
1253
- };
1254
- GlobeCanvas.displayName = "Globe.Canvas";
1255
- var GlobeDebug = ({ position = "topleft" }) => {
1256
- const { size, zoom, translation, rotation } = useGlobeContext();
1257
- return /* @__PURE__ */ React2.createElement("div", {
1258
- className: mx("z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded-sm", controlPositions[position])
1259
- }, /* @__PURE__ */ React2.createElement("pre", {
1260
- className: "font-mono text-xs text-green-700"
1261
- }, JSON.stringify({
1262
- size,
1263
- zoom,
1264
- translation,
1265
- rotation
1266
- }, null, 2)));
1267
- };
1268
- var GlobePanel = ({ position, classNames, children }) => {
1269
- return /* @__PURE__ */ React2.createElement("div", {
1270
- className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
1271
- }, children);
1272
- };
1273
- var CustomControl = ({ position, children }) => {
1274
- return /* @__PURE__ */ React2.createElement("div", {
1275
- className: mx("z-10 absolute overflow-hidden", controlPositions[position])
1276
- }, children);
1277
- };
1278
- var GlobeZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React2.createElement(CustomControl, {
1279
- position,
1280
- ...props
1281
- }, /* @__PURE__ */ React2.createElement(ZoomControls, {
1282
- onAction
1283
- }));
1284
- var GlobeAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React2.createElement(CustomControl, {
1285
- position,
1286
- ...props
1287
- }, /* @__PURE__ */ React2.createElement(ActionControls, {
1288
- onAction
1289
- }));
1290
- var Globe = {
1291
- Root: GlobeRoot,
1292
- Viewport: GlobeViewport,
1293
- Canvas: GlobeCanvas,
1294
- Zoom: GlobeZoom,
1295
- Action: GlobeAction,
1296
- Debug: GlobeDebug,
1297
- Panel: GlobePanel
1298
- };
1299
-
1300
- // src/components/Map/Map.tsx
1301
- import "leaflet/dist/leaflet.css";
1302
- import { createContext as createContext2 } from "@radix-ui/react-context";
1303
- import L, { Control, DomEvent, DomUtil, latLngBounds, point } from "leaflet";
1304
- import React3, { forwardRef as forwardRef2, useCallback as useCallback4, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useRef as useRef2, useState as useState5 } from "react";
1305
- import { createRoot } from "react-dom/client";
1306
- import { MapContainer, Marker, Polyline, Popup, TileLayer, useMap, useMapEvents } from "react-leaflet";
1307
- import { ThemeProvider, Tooltip } from "@dxos/react-ui";
1308
- import { composable as composable2, composableProps as composableProps2, defaultTx } from "@dxos/react-ui";
1309
- import { mx as mx2 } from "@dxos/ui-theme";
1310
- var defaults = {
1311
- center: {
1312
- lat: 51,
1313
- lng: 0
1314
- },
1315
- zoom: 4
1316
- };
1317
- var [MapContextProvider, useMapContext] = createContext2("Map");
1318
- var MapRoot = /* @__PURE__ */ forwardRef2(({ children, onChange }, forwardedRef) => {
1319
- const mapRef = useRef2(null);
1320
- const registerMap = useCallback4((map) => {
1321
- mapRef.current = map;
1322
- }, []);
1323
- useImperativeHandle2(forwardedRef, () => ({
1324
- getCenter: () => {
1325
- const center = mapRef.current?.getCenter();
1326
- return center ? {
1327
- lat: center.lat,
1328
- lng: center.lng
1329
- } : void 0;
1330
- },
1331
- getZoom: () => mapRef.current?.getZoom(),
1332
- setCenter: (center, zoom) => {
1333
- mapRef.current?.setView(center, zoom);
1334
- },
1335
- setZoom: (cb) => {
1336
- mapRef.current?.setZoom(cb(mapRef.current?.getZoom() ?? 0));
1337
- }
1338
- }), []);
1339
- const attention = false;
1340
- return /* @__PURE__ */ React3.createElement(MapContextProvider, {
1341
- attention,
1342
- onChange,
1343
- registerMap
1344
- }, children);
1345
- });
1346
- MapRoot.displayName = "Map.Root";
1347
- var MAP_VIEWPORT_NAME = "Map.Viewport";
1348
- var MapResize = () => {
1349
- const map = useMap();
1350
- useEffect7(() => {
1351
- const container = map.getContainer();
1352
- let frame = 0;
1353
- const observer = new ResizeObserver(() => {
1354
- cancelAnimationFrame(frame);
1355
- frame = requestAnimationFrame(() => map.invalidateSize());
1356
- });
1357
- observer.observe(container);
1358
- return () => {
1359
- cancelAnimationFrame(frame);
1360
- observer.disconnect();
1361
- };
1362
- }, [
1363
- map
1364
- ]);
1365
- return null;
1366
- };
1367
- var PINCH_ZOOM_SENSITIVITY = 0.03;
1368
- var MapPinchZoom = () => {
1369
- const map = useMap();
1370
- useEffect7(() => {
1371
- const container = map.getContainer();
1372
- let frame = 0;
1373
- let point2;
1374
- let target;
1375
- const onWheel = (event) => {
1376
- if (!event.ctrlKey) {
1377
- return;
1378
- }
1379
- event.preventDefault();
1380
- const rect = container.getBoundingClientRect();
1381
- point2 = L.point(event.clientX - rect.left, event.clientY - rect.top);
1382
- target = (target ?? map.getZoom()) - event.deltaY * PINCH_ZOOM_SENSITIVITY;
1383
- if (!frame) {
1384
- frame = requestAnimationFrame(() => {
1385
- frame = 0;
1386
- if (target !== void 0 && point2) {
1387
- map.setZoomAround(point2, target, {
1388
- animate: false
1389
- });
1390
- target = void 0;
1391
- }
1392
- });
1393
- }
1394
- };
1395
- container.addEventListener("wheel", onWheel, {
1396
- passive: false
1397
- });
1398
- return () => {
1399
- container.removeEventListener("wheel", onWheel);
1400
- cancelAnimationFrame(frame);
1401
- };
1402
- }, [
1403
- map
1404
- ]);
1405
- return null;
1406
- };
1407
- var MapViewport = composable2((props, _forwardedRef) => {
1408
- const { scrollWheelZoom = true, doubleClickZoom = true, touchZoom = true, center, zoom, whenReady, children, ...rest } = props;
1409
- const { attention, registerMap } = useMapContext(MAP_VIEWPORT_NAME);
1410
- const [map, setMap] = useState5(null);
1411
- const setMapRef = useCallback4((next) => {
1412
- setMap(next);
1413
- registerMap(next);
1414
- }, [
1415
- registerMap
1416
- ]);
1417
- useEffect7(() => {
1418
- if (!map) {
1419
- return;
1420
- }
1421
- if (attention) {
1422
- map.scrollWheelZoom.enable();
1423
- } else {
1424
- map.scrollWheelZoom.disable();
1425
- }
1426
- }, [
1427
- map,
1428
- attention
1429
- ]);
1430
- return /* @__PURE__ */ React3.createElement(MapContainer, {
1431
- ...composableProps2(rest, {
1432
- // Frame classes (formerly on Map.Root): focusable grid container.
1433
- classNames: "dx-container group relative grid dx-focus-ring-inset bg-base-surface!"
1434
- }),
1435
- attributionControl: false,
1436
- zoomControl: false,
1437
- scrollWheelZoom,
1438
- doubleClickZoom,
1439
- touchZoom,
1440
- // Allow fractional zoom so trackpad pinch (small ctrl+wheel deltas) isn't rounded away.
1441
- zoomSnap: 0,
1442
- center: center ?? defaults.center,
1443
- zoom: zoom ?? defaults.zoom,
1444
- whenReady,
1445
- ref: setMapRef
1446
- }, /* @__PURE__ */ React3.createElement(MapResize, null), /* @__PURE__ */ React3.createElement(MapPinchZoom, null), children);
1447
- });
1448
- MapViewport.displayName = "Map.Viewport";
1449
- var MAP_TILES_NAME = "Map.Tiles";
1450
- var DEFAULT_TILE_URL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
1451
- var MapTiles = ({ url = DEFAULT_TILE_URL }) => {
1452
- const ref = useRef2(null);
1453
- const { onChange } = useMapContext(MAP_TILES_NAME);
1454
- useMapEvents({
1455
- moveend: (ev) => {
1456
- onChange?.({
1457
- center: ev.target.getCenter(),
1458
- zoom: ev.target.getZoom()
1459
- });
1460
- }
1461
- });
1462
- const { attention } = useMapContext(MAP_TILES_NAME);
1463
- useEffect7(() => {
1464
- if (ref.current) {
1465
- ref.current.getContainer().dataset.attention = attention ? "1" : "0";
1466
- }
1467
- }, [
1468
- attention
1469
- ]);
1470
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(TileLayer, {
1471
- ref,
1472
- "data-attention": attention,
1473
- detectRetina: true,
1474
- className: 'dark:grayscale dark:invert data-[attention="0"]:!opacity-80',
1475
- url,
1476
- keepBuffer: 4
1477
- }));
1478
- };
1479
- MapTiles.displayName = MAP_TILES_NAME;
1480
- var MapMarkers = ({ selected, markers, lines, onSelect }) => {
1481
- const map = useMap();
1482
- useEffect7(() => {
1483
- const points = [
1484
- ...markers?.map((marker) => marker.location) ?? [],
1485
- ...lines?.flatMap((line) => [
1486
- line.source,
1487
- line.target
1488
- ]) ?? []
1489
- ];
1490
- if (points.length > 0) {
1491
- const bounds = latLngBounds(points);
1492
- const size = map.getSize();
1493
- const padding = Math.max(48, Math.min(size.x, size.y) / 6);
1494
- map.fitBounds(bounds, {
1495
- padding: point(padding, padding),
1496
- animate: false
1497
- });
1498
- }
1499
- }, [
1500
- markers,
1501
- lines,
1502
- map
1503
- ]);
1504
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, markers?.map(({ id, title, location: { lat, lng } }) => {
1505
- return /* @__PURE__ */ React3.createElement(Marker, {
1506
- key: id,
1507
- position: {
1508
- lat,
1509
- lng
1510
- },
1511
- eventHandlers: onSelect ? {
1512
- click: () => onSelect(id)
1513
- } : void 0,
1514
- icon: (
1515
- // TODO(burdon): Create custom icon from bundled assets.
1516
- // TODO(burdon): Selection state.
1517
- new L.Icon({
1518
- iconUrl: "https://dxos.network/marker-icon.png",
1519
- iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1520
- shadowUrl: "https://dxos.network/marker-shadow.png",
1521
- iconSize: [
1522
- 25,
1523
- 41
1524
- ],
1525
- iconAnchor: [
1526
- 12,
1527
- 41
1528
- ],
1529
- popupAnchor: [
1530
- 1,
1531
- -34
1532
- ],
1533
- shadowSize: [
1534
- 41,
1535
- 41
1536
- ]
1537
- })
1538
- )
1539
- }, title && /* @__PURE__ */ React3.createElement(Popup, null, title));
1540
- }));
1541
- };
1542
- MapMarkers.displayName = "Map.Markers";
1543
- var MapLines = ({ lines }) => {
1544
- if (!lines || lines.length === 0) {
1545
- return null;
1546
- }
1547
- const polylines = [];
1548
- for (const { source, target, color } of lines) {
1549
- const last = polylines[polylines.length - 1];
1550
- const lastPos = last?.positions[last.positions.length - 1];
1551
- if (last && last.color === color && lastPos?.lat === source.lat && lastPos?.lng === source.lng) {
1552
- last.positions.push(target);
1553
- } else {
1554
- polylines.push({
1555
- positions: [
1556
- source,
1557
- target
1558
- ],
1559
- color
1560
- });
1561
- }
1562
- }
1563
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, polylines.map(({ positions, color }, index) => /* @__PURE__ */ React3.createElement(Polyline, {
1564
- key: index,
1565
- positions,
1566
- pathOptions: {
1567
- color,
1568
- weight: 4,
1569
- opacity: 0.8
1570
- }
1571
- })));
1572
- };
1573
- MapLines.displayName = "Map.Lines";
1574
- var CustomControl2 = ({ position, children }) => {
1575
- const map = useMap();
1576
- const rootRef = useRef2(void 0);
1577
- useEffect7(() => {
1578
- const control = new Control({
1579
- position
1580
- });
1581
- control.onAdd = () => {
1582
- const container = DomUtil.create("div", mx2("m-0!", controlPositions[position]));
1583
- DomEvent.disableClickPropagation(container);
1584
- DomEvent.disableScrollPropagation(container);
1585
- const root = createRoot(container);
1586
- rootRef.current = root;
1587
- root.render(/* @__PURE__ */ React3.createElement(ThemeProvider, {
1588
- tx: defaultTx
1589
- }, /* @__PURE__ */ React3.createElement(Tooltip.Provider, null, children)));
1590
- return container;
1591
- };
1592
- control.addTo(map);
1593
- return () => {
1594
- control.remove();
1595
- const root = rootRef.current;
1596
- rootRef.current = void 0;
1597
- queueMicrotask(() => root?.unmount());
1598
- };
1599
- }, [
1600
- map,
1601
- position
1602
- ]);
1603
- useEffect7(() => {
1604
- rootRef.current?.render(/* @__PURE__ */ React3.createElement(ThemeProvider, {
1605
- tx: defaultTx
1606
- }, /* @__PURE__ */ React3.createElement(Tooltip.Provider, null, children)));
1607
- }, [
1608
- children
1609
- ]);
1610
- return null;
1611
- };
1612
- var MapZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl2, {
1613
- position,
1614
- ...props
1615
- }, /* @__PURE__ */ React3.createElement(ZoomControls, {
1616
- onAction
1617
- }));
1618
- var MapAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl2, {
1619
- position,
1620
- ...props
1621
- }, /* @__PURE__ */ React3.createElement(ActionControls, {
1622
- onAction
1623
- }));
1624
- var Map2 = {
1625
- Root: MapRoot,
1626
- Viewport: MapViewport,
1627
- Tiles: MapTiles,
1628
- Markers: MapMarkers,
1629
- Lines: MapLines,
1630
- Zoom: MapZoom,
1631
- Action: MapAction
1632
- };
1633
- export {
1634
- ActionControls,
1635
- DEFAULT_TILE_URL,
1636
- Globe,
1637
- GlobeContext,
1638
- Map2 as Map,
1639
- ZoomControls,
1640
- closestPoint,
1641
- controlPositions,
1642
- createLayers,
1643
- createRotationTween,
1644
- flyDuration,
1645
- geoCircle,
1646
- geoInertiaDrag,
1647
- geoLine,
1648
- geoPoint,
1649
- geoToPosition,
1650
- getDistance,
1651
- globeStyles,
1652
- positionToRotation,
1653
- renderLayers,
1654
- restrictAxis,
1655
- timer,
1656
- useDrag,
1657
- useGlobeContext,
1658
- useGlobeZoomHandler,
1659
- useMapZoomHandler,
1660
- useSimplifiedTopology,
1661
- useSpinner,
1662
- useTopology,
1663
- useTour,
1664
- useWheel
1665
- };
1666
- //# sourceMappingURL=index.mjs.map