@dxos/react-ui-geo 0.10.0 → 0.11.1

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