@dxos/solid-ui-geo 0.0.0 → 0.8.4-main.59c2e9b

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 (76) hide show
  1. package/dist/lib/browser/chunk-KONL6N4F.mjs +9 -0
  2. package/dist/lib/browser/chunk-KONL6N4F.mjs.map +7 -0
  3. package/dist/lib/browser/countries-110m-6FO6R2F2.mjs +37859 -0
  4. package/dist/lib/browser/countries-110m-6FO6R2F2.mjs.map +7 -0
  5. package/dist/lib/browser/data.mjs +7 -0
  6. package/dist/lib/browser/data.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +1172 -0
  8. package/dist/lib/browser/index.mjs.map +7 -0
  9. package/dist/lib/browser/meta.json +1 -0
  10. package/dist/lib/node-esm/chunk-KNEGK7HI.mjs +11 -0
  11. package/dist/lib/node-esm/chunk-KNEGK7HI.mjs.map +7 -0
  12. package/dist/lib/node-esm/countries-110m-UQ4O6NQR.mjs +37861 -0
  13. package/dist/lib/node-esm/countries-110m-UQ4O6NQR.mjs.map +7 -0
  14. package/dist/lib/node-esm/data.mjs +8 -0
  15. package/dist/lib/node-esm/data.mjs.map +7 -0
  16. package/dist/lib/node-esm/index.mjs +1173 -0
  17. package/dist/lib/node-esm/index.mjs.map +7 -0
  18. package/dist/lib/node-esm/meta.json +1 -0
  19. package/dist/types/data/airports.d.ts +18 -0
  20. package/dist/types/data/airports.d.ts.map +1 -0
  21. package/dist/types/data/countries-110m.d.ts +36 -0
  22. package/dist/types/data/countries-110m.d.ts.map +1 -0
  23. package/dist/types/data/countries-dots-3.d.ts +9 -0
  24. package/dist/types/data/countries-dots-3.d.ts.map +1 -0
  25. package/dist/types/src/components/Globe/Globe.d.ts +54 -0
  26. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -0
  27. package/dist/types/src/components/Globe/Globe.solid-stories.d.ts +29 -0
  28. package/dist/types/src/components/Globe/Globe.solid-stories.d.ts.map +1 -0
  29. package/dist/types/src/components/Globe/index.d.ts +2 -0
  30. package/dist/types/src/components/Globe/index.d.ts.map +1 -0
  31. package/dist/types/src/components/Map/Map.d.ts +38 -0
  32. package/dist/types/src/components/Map/Map.d.ts.map +1 -0
  33. package/dist/types/src/components/Map/Map.solid-stories.d.ts +17 -0
  34. package/dist/types/src/components/Map/Map.solid-stories.d.ts.map +1 -0
  35. package/dist/types/src/components/Map/index.d.ts +2 -0
  36. package/dist/types/src/components/Map/index.d.ts.map +1 -0
  37. package/dist/types/src/components/Toolbar/Controls.d.ts +11 -0
  38. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -0
  39. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  40. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  41. package/dist/types/src/components/index.d.ts +4 -0
  42. package/dist/types/src/components/index.d.ts.map +1 -0
  43. package/dist/types/src/data.d.ts +3 -0
  44. package/dist/types/src/data.d.ts.map +1 -0
  45. package/dist/types/src/hooks/context.d.ts +33 -0
  46. package/dist/types/src/hooks/context.d.ts.map +1 -0
  47. package/dist/types/src/hooks/index.d.ts +7 -0
  48. package/dist/types/src/hooks/index.d.ts.map +1 -0
  49. package/dist/types/src/hooks/useDrag.d.ts +16 -0
  50. package/dist/types/src/hooks/useDrag.d.ts.map +1 -0
  51. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +3 -0
  52. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -0
  53. package/dist/types/src/hooks/useMapZoomHandler.d.ts +3 -0
  54. package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -0
  55. package/dist/types/src/hooks/useSpinner.d.ts +14 -0
  56. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -0
  57. package/dist/types/src/hooks/useTour.d.ts +22 -0
  58. package/dist/types/src/hooks/useTour.d.ts.map +1 -0
  59. package/dist/types/src/index.d.ts +7 -0
  60. package/dist/types/src/index.d.ts.map +1 -0
  61. package/dist/types/src/translations.d.ts +12 -0
  62. package/dist/types/src/translations.d.ts.map +1 -0
  63. package/dist/types/src/types.d.ts +8 -0
  64. package/dist/types/src/types.d.ts.map +1 -0
  65. package/dist/types/src/util/debug.d.ts +2 -0
  66. package/dist/types/src/util/debug.d.ts.map +1 -0
  67. package/dist/types/src/util/index.d.ts +5 -0
  68. package/dist/types/src/util/index.d.ts.map +1 -0
  69. package/dist/types/src/util/inertia.d.ts +15 -0
  70. package/dist/types/src/util/inertia.d.ts.map +1 -0
  71. package/dist/types/src/util/path.d.ts +12 -0
  72. package/dist/types/src/util/path.d.ts.map +1 -0
  73. package/dist/types/src/util/render.d.ts +26 -0
  74. package/dist/types/src/util/render.d.ts.map +1 -0
  75. package/dist/types/tsconfig.tsbuildinfo +1 -0
  76. package/package.json +12 -8
@@ -0,0 +1,1172 @@
1
+ import {
2
+ loadTopology
3
+ } from "./chunk-KONL6N4F.mjs";
4
+
5
+ // src/components/Globe/Globe.tsx
6
+ import { jsx as _jsx3 } from "solid-js/jsx-runtime";
7
+ import { createResizeObserver } from "@solid-primitives/resize-observer";
8
+ import { easeLinear, easeSinOut, geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition } from "d3";
9
+ import { Show, createEffect as createEffect5, createMemo, createSignal as createSignal4 } from "solid-js";
10
+
11
+ // src/hooks/context.tsx
12
+ import { jsx as _jsx } from "solid-js/jsx-runtime";
13
+ import { createContext, createEffect, createSignal, useContext } from "solid-js";
14
+ import { raise } from "@dxos/debug";
15
+ var defaults = {
16
+ center: {
17
+ lat: 51,
18
+ lng: 0
19
+ },
20
+ zoom: 4
21
+ };
22
+ var GlobeContext = createContext();
23
+ var GlobeContextProvider = (props) => {
24
+ const [size, setSize] = createSignal(props.size ?? {
25
+ width: 0,
26
+ height: 0
27
+ });
28
+ const [center, setCenter] = createSignal(props.center ?? defaults.center);
29
+ const [zoom, setZoom] = createSignal(props.zoom ?? defaults.zoom);
30
+ const [translation, setTranslation] = createSignal(props.translation);
31
+ const [rotation, setRotation] = createSignal(props.rotation);
32
+ createEffect(() => {
33
+ if (props.size) {
34
+ setSize(props.size);
35
+ }
36
+ });
37
+ return /* @__PURE__ */ _jsx(GlobeContext.Provider, {
38
+ value: {
39
+ size,
40
+ center,
41
+ zoom,
42
+ translation,
43
+ rotation,
44
+ setCenter,
45
+ setZoom,
46
+ setTranslation,
47
+ setRotation
48
+ },
49
+ children: props.children
50
+ });
51
+ };
52
+ var useGlobeContext = () => {
53
+ return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
54
+ };
55
+
56
+ // src/hooks/useDrag.ts
57
+ import { select as select2 } from "d3";
58
+ import { createEffect as createEffect2, onCleanup } from "solid-js";
59
+
60
+ // src/util/debug.ts
61
+ var debug = false;
62
+ var timer = (cb) => {
63
+ const start = Date.now();
64
+ const data = cb();
65
+ const t = Date.now() - start / 1e3;
66
+ if (debug) {
67
+ console.log({
68
+ t,
69
+ data
70
+ });
71
+ }
72
+ return data;
73
+ };
74
+
75
+ // src/util/inertia.ts
76
+ import { drag, select, timer as timer2 } from "d3";
77
+ import versor from "versor";
78
+ var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
79
+ var geoInertiaDrag = (target, render, projection, options) => {
80
+ if (!options) {
81
+ options = {};
82
+ }
83
+ if (target.node) {
84
+ target = target.node();
85
+ }
86
+ target = select(target);
87
+ const inertia = geoInertiaDragHelper({
88
+ projection,
89
+ render: (rotation) => {
90
+ projection.rotate(rotation);
91
+ render && render();
92
+ },
93
+ axis: restrictAxis(options.xAxis ? [
94
+ true,
95
+ false,
96
+ false
97
+ ] : [
98
+ true,
99
+ true,
100
+ true
101
+ ]),
102
+ start: options.start,
103
+ move: options.move,
104
+ end: options.end,
105
+ stop: options.stop,
106
+ finish: options.finish,
107
+ time: options.time,
108
+ hold: options.hold
109
+ });
110
+ target.call(drag().on("start", inertia.start).on("drag", inertia.move).on("end", inertia.end));
111
+ return inertia;
112
+ };
113
+ var geoInertiaDragHelper = (opt) => {
114
+ const projection = opt.projection;
115
+ let v0;
116
+ let r0;
117
+ let q0;
118
+ let v10;
119
+ let v11;
120
+ let q10;
121
+ const inertia = inertiaHelper({
122
+ axis: opt.axis,
123
+ start: () => {
124
+ v0 = versor.cartesian(projection.invert(inertia.position));
125
+ r0 = projection.rotate();
126
+ q0 = versor(r0);
127
+ opt.start && opt.start();
128
+ },
129
+ move: () => {
130
+ const inv = projection.rotate(r0).invert(inertia.position);
131
+ if (isNaN(inv[0])) {
132
+ return;
133
+ }
134
+ const v1 = versor.cartesian(inv);
135
+ const q1 = versor.multiply(q0, versor.delta(v0, v1));
136
+ const r1 = versor.rotation(q1);
137
+ const r2 = opt.axis(r0, r1);
138
+ opt.render(r2);
139
+ opt.move && opt.move();
140
+ },
141
+ end: () => {
142
+ v10 = versor.cartesian(projection.invert(inertia.position.map((d, i) => d - inertia.velocity[i] / 1e3)));
143
+ q10 = versor(projection.rotate());
144
+ v11 = versor.cartesian(projection.invert(inertia.position));
145
+ opt.end && opt.end();
146
+ },
147
+ stop: opt.stop,
148
+ finish: opt.finish,
149
+ render: (t) => {
150
+ const r1 = versor.rotation(versor.multiply(q10, versor.delta(v10, v11, t * 1e3)));
151
+ const r2 = opt.axis(r0, r1);
152
+ opt.render && opt.render(r2);
153
+ },
154
+ time: opt.time
155
+ });
156
+ return inertia;
157
+ };
158
+ function inertiaHelper(opt) {
159
+ const A = opt.time || 5e3;
160
+ const limit = 1.0001;
161
+ const B = -Math.log(1 - 1 / limit);
162
+ const inertia = {
163
+ position: [
164
+ 0,
165
+ 0
166
+ ],
167
+ velocity: [
168
+ 0,
169
+ 0
170
+ ],
171
+ timer: timer2(() => {
172
+ }),
173
+ time: 0,
174
+ t: 0,
175
+ start: function(ev) {
176
+ const position = [
177
+ ev.x,
178
+ ev.y
179
+ ];
180
+ inertia.position = position;
181
+ inertia.velocity = [
182
+ 0,
183
+ 0
184
+ ];
185
+ inertia.timer.stop();
186
+ this.classList.remove("inertia");
187
+ this.classList.add("dragging");
188
+ opt.start && opt.start.call(this, position);
189
+ },
190
+ move: function(ev) {
191
+ const position = [
192
+ ev.x,
193
+ ev.y
194
+ ];
195
+ const time = performance.now();
196
+ const deltaTime = time - inertia.time;
197
+ const decay = 1 - Math.exp(-deltaTime / 1e3);
198
+ inertia.velocity = inertia.velocity.map((d, i) => {
199
+ const deltaPos = position[i] - inertia.position[i];
200
+ const deltaTime2 = time - inertia.time;
201
+ return 1e3 * (1 - decay) * deltaPos / deltaTime2 + d * decay;
202
+ });
203
+ inertia.velocity = opt.axis([
204
+ 0,
205
+ 0
206
+ ], inertia.velocity);
207
+ inertia.time = time;
208
+ inertia.position = position;
209
+ opt.move && opt.move.call(this, position);
210
+ },
211
+ end: function(ev) {
212
+ this.classList.remove("dragging", "inertia");
213
+ const v = inertia.velocity;
214
+ if (v[0] * v[0] + v[1] * v[1] < 100) {
215
+ inertia.timer.stop();
216
+ return opt.stop && opt.stop();
217
+ }
218
+ const time = performance.now();
219
+ const deltaTime = time - inertia.time;
220
+ if (opt.hold === void 0) {
221
+ opt.hold = 100;
222
+ }
223
+ if (deltaTime >= opt.hold) {
224
+ inertia.timer.stop();
225
+ return opt.stop && opt.stop();
226
+ }
227
+ this.classList.add("inertia");
228
+ opt.end && opt.end();
229
+ const self = this;
230
+ inertia.timer.restart((e) => {
231
+ inertia.t = limit * (1 - Math.exp(-B * e / A));
232
+ opt.render && opt.render(inertia.t);
233
+ if (inertia.t > 1) {
234
+ inertia.timer.stop();
235
+ self.classList.remove("inertia");
236
+ inertia.velocity = [
237
+ 0,
238
+ 0
239
+ ];
240
+ inertia.t = 1;
241
+ opt.finish && opt.finish();
242
+ }
243
+ });
244
+ }
245
+ };
246
+ inertia.timer.stop();
247
+ return inertia;
248
+ }
249
+
250
+ // src/util/path.ts
251
+ import { geoCircle as d3GeoCircle } from "d3";
252
+ var positionToRotation = ([lng, lat], tilt = 0) => [
253
+ -lng,
254
+ tilt - lat,
255
+ 0
256
+ ];
257
+ var geoToPosition = ({ lat, lng }) => [
258
+ lng,
259
+ lat
260
+ ];
261
+ var geoPoint = (point) => ({
262
+ type: "Point",
263
+ coordinates: geoToPosition(point)
264
+ });
265
+ var geoCircle = ({ lat, lng }, radius) => d3GeoCircle().radius(radius).center([
266
+ lng,
267
+ lat
268
+ ])();
269
+ var geoLine = (p1, p2) => ({
270
+ type: "LineString",
271
+ coordinates: [
272
+ [
273
+ p1.lng,
274
+ p1.lat
275
+ ],
276
+ [
277
+ p2.lng,
278
+ p2.lat
279
+ ]
280
+ ]
281
+ });
282
+ var closestPoint = (points, target) => {
283
+ if (points.length === 0) {
284
+ return target;
285
+ }
286
+ let closestPoint2 = points[0];
287
+ let minDistance = getDistance(points[0], target);
288
+ for (const point of points) {
289
+ const distance = getDistance(point, target);
290
+ if (distance < minDistance) {
291
+ minDistance = distance;
292
+ closestPoint2 = point;
293
+ }
294
+ }
295
+ return closestPoint2;
296
+ };
297
+ var getDistance = (point1, point2) => {
298
+ const dx = point1[0] - point2[0];
299
+ const dy = point1[1] - point2[1];
300
+ return Math.sqrt(dx * dx + dy * dy);
301
+ };
302
+
303
+ // src/util/render.ts
304
+ import { geoGraticule } from "d3";
305
+ import { feature, mesh } from "topojson-client";
306
+ var createLayers = (topology, features, styles) => {
307
+ const layers = [];
308
+ if (styles.water) {
309
+ layers.push({
310
+ styles: styles.water,
311
+ path: {
312
+ type: "Sphere"
313
+ }
314
+ });
315
+ }
316
+ if (styles.graticule) {
317
+ layers.push({
318
+ styles: styles.graticule,
319
+ path: geoGraticule().step([
320
+ 6,
321
+ 6
322
+ ])()
323
+ });
324
+ }
325
+ if (topology) {
326
+ if (topology.objects.land && styles.land) {
327
+ layers.push({
328
+ styles: styles.land,
329
+ path: feature(topology, topology.objects.land)
330
+ });
331
+ }
332
+ if (topology.objects.countries && styles.border) {
333
+ layers.push({
334
+ styles: styles.border,
335
+ path: mesh(topology, topology.objects.countries, (a, b) => a !== b)
336
+ });
337
+ }
338
+ if (topology.objects.dots && styles.dots) {
339
+ layers.push({
340
+ styles: styles.dots,
341
+ path: topology.objects.dots
342
+ });
343
+ }
344
+ }
345
+ if (features) {
346
+ const { points, lines } = features;
347
+ if (points && styles.point) {
348
+ layers.push({
349
+ styles: styles.point,
350
+ path: {
351
+ type: "GeometryCollection",
352
+ geometries: points.map((point) => geoPoint(point))
353
+ }
354
+ });
355
+ }
356
+ if (lines && styles.line) {
357
+ layers.push({
358
+ styles: styles.line,
359
+ path: {
360
+ type: "GeometryCollection",
361
+ geometries: lines.map(({ source, target }) => geoLine(source, target))
362
+ }
363
+ });
364
+ }
365
+ }
366
+ return layers;
367
+ };
368
+ var renderLayers = (generator, layers = [], scale, styles) => {
369
+ const context = generator.context();
370
+ const { canvas: { width, height } } = context;
371
+ context.reset();
372
+ if (styles.background) {
373
+ context.fillStyle = styles.background.fillStyle;
374
+ context.fillRect(0, 0, width, height);
375
+ } else {
376
+ context.clearRect(0, 0, width, height);
377
+ }
378
+ layers.forEach(({ path, styles: styles2 }) => {
379
+ context.save();
380
+ let fill = false;
381
+ let stroke = false;
382
+ if (styles2) {
383
+ Object.entries(styles2).forEach(([key, value]) => {
384
+ if (key === "pointRadius") {
385
+ generator.pointRadius(value * scale);
386
+ } else {
387
+ context[key] = value;
388
+ fill ||= key === "fillStyle";
389
+ stroke ||= key === "strokeStyle";
390
+ }
391
+ });
392
+ }
393
+ context.beginPath();
394
+ generator(path);
395
+ fill && context.fill();
396
+ stroke && context.stroke();
397
+ context.restore();
398
+ });
399
+ return context;
400
+ };
401
+
402
+ // src/hooks/useDrag.ts
403
+ var useDrag = (controller, options = {}) => {
404
+ createEffect2(() => {
405
+ const canvas = controller?.canvas;
406
+ if (!canvas || options.disabled) {
407
+ return;
408
+ }
409
+ geoInertiaDrag(select2(canvas), () => {
410
+ controller.setRotation(controller.projection.rotate());
411
+ options.onUpdate?.({
412
+ type: "move",
413
+ controller
414
+ });
415
+ }, controller.projection, {
416
+ xAxis: options.xAxis,
417
+ time: 3e3,
418
+ start: () => options.onUpdate?.({
419
+ type: "start",
420
+ controller
421
+ }),
422
+ finish: () => options.onUpdate?.({
423
+ type: "end",
424
+ controller
425
+ })
426
+ });
427
+ onCleanup(() => {
428
+ cancelDrag(select2(canvas));
429
+ });
430
+ });
431
+ };
432
+ var cancelDrag = (node) => node.on(".drag", null);
433
+
434
+ // src/hooks/useGlobeZoomHandler.ts
435
+ var ZOOM_FACTOR = 0.1;
436
+ var useGlobeZoomHandler = (controller) => {
437
+ return (event) => {
438
+ if (!controller) {
439
+ return;
440
+ }
441
+ switch (event) {
442
+ case "zoom-in": {
443
+ controller.setZoom((zoom) => {
444
+ return zoom * (1 + ZOOM_FACTOR);
445
+ });
446
+ break;
447
+ }
448
+ case "zoom-out": {
449
+ controller.setZoom((zoom) => {
450
+ return zoom * (1 - ZOOM_FACTOR);
451
+ });
452
+ break;
453
+ }
454
+ }
455
+ };
456
+ };
457
+
458
+ // src/hooks/useMapZoomHandler.ts
459
+ var useMapZoomHandler = (controller) => {
460
+ return (event) => {
461
+ if (!controller) {
462
+ return;
463
+ }
464
+ switch (event) {
465
+ case "zoom-in": {
466
+ controller.setZoom((scale) => scale + 1);
467
+ break;
468
+ }
469
+ case "zoom-out": {
470
+ controller.setZoom((scale) => scale - 1);
471
+ break;
472
+ }
473
+ }
474
+ };
475
+ };
476
+
477
+ // src/hooks/useSpinner.ts
478
+ import { timer as d3Timer } from "d3";
479
+ import { createEffect as createEffect3, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
480
+ var useSpinner = (controller, options = {}) => {
481
+ const [running, setRunning] = createSignal2(false);
482
+ createEffect3(() => {
483
+ let timer3;
484
+ const start = () => {
485
+ const delta = options.delta ?? [
486
+ 1e-3,
487
+ 0,
488
+ 0
489
+ ];
490
+ let t = 0;
491
+ let lastRotation = controller.projection.rotate();
492
+ timer3 = d3Timer((elapsed) => {
493
+ const dt = elapsed - t;
494
+ t = elapsed;
495
+ const rotation = [
496
+ lastRotation[0] + delta[0] * dt,
497
+ lastRotation[1] + delta[1] * dt,
498
+ lastRotation[2] + delta[2] * dt
499
+ ];
500
+ lastRotation = rotation;
501
+ controller.setRotation(rotation);
502
+ });
503
+ };
504
+ const stop = () => {
505
+ if (timer3) {
506
+ timer3.stop();
507
+ timer3 = void 0;
508
+ }
509
+ };
510
+ if (controller && running()) {
511
+ start();
512
+ } else {
513
+ stop();
514
+ }
515
+ onCleanup2(() => stop());
516
+ });
517
+ return {
518
+ start: () => {
519
+ if (!options.disabled) {
520
+ setRunning(true);
521
+ }
522
+ },
523
+ stop: () => setRunning(false)
524
+ };
525
+ };
526
+
527
+ // src/hooks/useTour.ts
528
+ import { selection as d3Selection, geoDistance, geoInterpolate, geoPath } from "d3";
529
+ import { createEffect as createEffect4, createSignal as createSignal3, onCleanup as onCleanup3 } from "solid-js";
530
+ import versor2 from "versor";
531
+ var TRANSITION_NAME = "globe-tour";
532
+ var defaultDuration = 1500;
533
+ var useTour = (controller, points, options = {}) => {
534
+ const selection = d3Selection();
535
+ const [running, setRunning] = createSignal3(options.running ?? false);
536
+ createEffect4(() => {
537
+ if (!running()) {
538
+ selection.interrupt(TRANSITION_NAME);
539
+ return;
540
+ }
541
+ let t;
542
+ if (controller && running()) {
543
+ t = setTimeout(async () => {
544
+ const { canvas, projection, setRotation } = controller;
545
+ const context = canvas.getContext("2d", {
546
+ alpha: false
547
+ });
548
+ const path = geoPath(projection, context).pointRadius(2);
549
+ const tilt = options.tilt ?? 0;
550
+ let last;
551
+ try {
552
+ const p = [
553
+ ...points ?? []
554
+ ];
555
+ if (options.loop) {
556
+ p.push(p[0]);
557
+ }
558
+ for (const next of p) {
559
+ if (!running()) {
560
+ break;
561
+ }
562
+ const p1 = last ? geoToPosition(last) : void 0;
563
+ const p2 = geoToPosition(next);
564
+ const ip = geoInterpolate(p1 || p2, p2);
565
+ const distance = geoDistance(p1 || p2, p2);
566
+ const r1 = p1 ? positionToRotation(p1, tilt) : controller.projection.rotate();
567
+ const r2 = positionToRotation(p2, tilt);
568
+ const iv = versor2.interpolate(r1, r2);
569
+ const transition2 = selection.transition(TRANSITION_NAME).duration(Math.max(options.duration ?? defaultDuration, distance * 2e3)).tween("render", () => (t2) => {
570
+ const t1 = Math.max(0, Math.min(1, t2 * 2 - 1));
571
+ const t22 = Math.min(1, t2 * 2);
572
+ context.save();
573
+ {
574
+ context.beginPath();
575
+ context.strokeStyle = options?.styles?.arc?.strokeStyle ?? "yellow";
576
+ context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.zoom ?? 1);
577
+ context.setLineDash(options?.styles?.arc?.lineDash ?? []);
578
+ path({
579
+ type: "LineString",
580
+ coordinates: [
581
+ ip(t1),
582
+ ip(t22)
583
+ ]
584
+ });
585
+ context.stroke();
586
+ context.beginPath();
587
+ context.fillStyle = options?.styles?.cursor?.fillStyle ?? "orange";
588
+ path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.zoom ?? 1));
589
+ path({
590
+ type: "Point",
591
+ coordinates: ip(t22)
592
+ });
593
+ context.fill();
594
+ }
595
+ context.restore();
596
+ projection.rotate(iv(t2));
597
+ setRotation(projection.rotate());
598
+ });
599
+ await transition2.end();
600
+ last = next;
601
+ }
602
+ } catch {
603
+ } finally {
604
+ setRunning(false);
605
+ }
606
+ });
607
+ onCleanup3(() => {
608
+ clearTimeout(t);
609
+ selection.interrupt(TRANSITION_NAME);
610
+ });
611
+ }
612
+ });
613
+ return {
614
+ running,
615
+ setRunning
616
+ };
617
+ };
618
+
619
+ // src/components/Toolbar/Controls.tsx
620
+ import { jsx as _jsx2, jsxs as _jsxs } from "solid-js/jsx-runtime";
621
+ var controlPositions = {
622
+ topleft: "top-2 left-2",
623
+ topright: "top-2 right-2",
624
+ bottomleft: "bottom-2 left-2",
625
+ bottomright: "bottom-2 right-2"
626
+ };
627
+ var ZoomControls = (props) => {
628
+ return /* @__PURE__ */ _jsxs("div", {
629
+ class: `flex flex-row gap-2 ${props.class ?? ""}`,
630
+ children: [
631
+ /* @__PURE__ */ _jsx2("button", {
632
+ type: "button",
633
+ class: "dx-button dx-focus-ring w-10 h-10 min-bs-[2.5rem] pli-3 rounded-sm flex items-center justify-center",
634
+ onClick: () => props.onAction?.("zoom-in"),
635
+ title: "Zoom in",
636
+ children: /* @__PURE__ */ _jsx2("span", {
637
+ class: "text-xl",
638
+ children: "+"
639
+ })
640
+ }),
641
+ /* @__PURE__ */ _jsx2("button", {
642
+ type: "button",
643
+ class: "dx-button dx-focus-ring w-10 h-10 min-bs-[2.5rem] pli-3 rounded-sm flex items-center justify-center",
644
+ onClick: () => props.onAction?.("zoom-out"),
645
+ title: "Zoom out",
646
+ children: /* @__PURE__ */ _jsx2("span", {
647
+ class: "text-xl",
648
+ children: "\u2212"
649
+ })
650
+ })
651
+ ]
652
+ });
653
+ };
654
+ var ActionControls = (props) => {
655
+ return /* @__PURE__ */ _jsxs("div", {
656
+ class: `flex flex-row gap-2 ${props.class ?? ""}`,
657
+ children: [
658
+ /* @__PURE__ */ _jsx2("button", {
659
+ type: "button",
660
+ class: "dx-button dx-focus-ring w-10 h-10 min-bs-[2.5rem] pli-3 rounded-sm flex items-center justify-center",
661
+ onClick: () => props.onAction?.("start"),
662
+ title: "Start",
663
+ children: /* @__PURE__ */ _jsx2("span", {
664
+ class: "text-xl",
665
+ children: "\u25B6"
666
+ })
667
+ }),
668
+ /* @__PURE__ */ _jsx2("button", {
669
+ type: "button",
670
+ class: "dx-button dx-focus-ring w-10 h-10 min-bs-[2.5rem] pli-3 rounded-sm flex items-center justify-center",
671
+ onClick: () => props.onAction?.("toggle"),
672
+ title: "Toggle",
673
+ children: /* @__PURE__ */ _jsx2("span", {
674
+ class: "text-xl",
675
+ children: "\u{1F30D}"
676
+ })
677
+ })
678
+ ]
679
+ });
680
+ };
681
+
682
+ // src/components/Globe/Globe.tsx
683
+ var defaultStyles = {
684
+ light: {
685
+ background: {
686
+ fillStyle: "#EEE"
687
+ },
688
+ water: {
689
+ fillStyle: "#555"
690
+ },
691
+ land: {
692
+ fillStyle: "#999"
693
+ },
694
+ line: {
695
+ strokeStyle: "darkred"
696
+ },
697
+ point: {
698
+ fillStyle: "#111111",
699
+ strokeStyle: "#111111",
700
+ strokeWidth: 1,
701
+ pointRadius: 0.5
702
+ }
703
+ },
704
+ dark: {
705
+ background: {
706
+ fillStyle: "#111111"
707
+ },
708
+ water: {
709
+ fillStyle: "#123E6A"
710
+ },
711
+ land: {
712
+ fillStyle: "#032153"
713
+ },
714
+ line: {
715
+ strokeStyle: "#111111"
716
+ },
717
+ point: {
718
+ fillStyle: "#111111",
719
+ strokeStyle: "#111111",
720
+ strokeWidth: 1,
721
+ pointRadius: 0.5
722
+ }
723
+ }
724
+ };
725
+ var projectionMap = {
726
+ orthographic: geoOrthographic,
727
+ mercator: geoMercator,
728
+ "transverse-mercator": geoTransverseMercator
729
+ };
730
+ var getProjection = (type = "orthographic") => {
731
+ if (typeof type === "string") {
732
+ const constructor = projectionMap[type] ?? geoOrthographic;
733
+ return constructor();
734
+ }
735
+ return type ?? geoOrthographic();
736
+ };
737
+ var GlobeRoot = (props) => {
738
+ let containerRef;
739
+ const [size, setSize] = createSignal4({
740
+ width: 0,
741
+ height: 0
742
+ });
743
+ createResizeObserver(() => containerRef, ({ width, height }) => {
744
+ setSize({
745
+ width,
746
+ height
747
+ });
748
+ });
749
+ return /* @__PURE__ */ _jsx3("div", {
750
+ ref: containerRef,
751
+ class: `relative flex grow overflow-hidden ${props.class ?? ""}`,
752
+ ...props,
753
+ children: /* @__PURE__ */ _jsx3(GlobeContextProvider, {
754
+ size: size(),
755
+ ...props,
756
+ children: props.children
757
+ })
758
+ });
759
+ };
760
+ var GlobeCanvas = (props) => {
761
+ const themeMode = "dark";
762
+ const styles = createMemo(() => props.styles ?? defaultStyles[themeMode]);
763
+ const [canvas, setCanvas] = createSignal4(null);
764
+ const projection = createMemo(() => getProjection(props.projection));
765
+ const layers = createMemo(() => {
766
+ return timer(() => createLayers(props.topology, props.features, styles()));
767
+ });
768
+ const { size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation } = useGlobeContext();
769
+ let zoomValue = zoom();
770
+ let zooming = false;
771
+ createEffect5(() => {
772
+ });
773
+ createEffect5(() => {
774
+ const c = center();
775
+ if (c) {
776
+ setZoom(1);
777
+ setRotation(positionToRotation(geoToPosition(c)));
778
+ }
779
+ });
780
+ createEffect5(() => {
781
+ const canvasEl = canvas();
782
+ if (canvasEl && props.ref) {
783
+ const controller = {
784
+ canvas: canvasEl,
785
+ projection: projection(),
786
+ get zoom() {
787
+ return zoomValue;
788
+ },
789
+ translation,
790
+ rotation,
791
+ setZoom: (s) => {
792
+ if (typeof s === "function") {
793
+ const is = interpolateNumber(zoomValue, s(zoomValue));
794
+ transition().ease(zooming ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => {
795
+ const newZoom = is(t);
796
+ zoomValue = newZoom;
797
+ setZoom(newZoom);
798
+ }).on("end", () => {
799
+ zooming = false;
800
+ });
801
+ } else {
802
+ zoomValue = s;
803
+ setZoom(s);
804
+ }
805
+ },
806
+ setTranslation,
807
+ setRotation
808
+ };
809
+ props.ref(controller);
810
+ }
811
+ });
812
+ const generator = createMemo(() => {
813
+ const canvasEl = canvas();
814
+ const proj = projection();
815
+ return canvasEl && proj && geoPath2(proj, canvasEl.getContext("2d", {
816
+ alpha: false
817
+ }));
818
+ });
819
+ createEffect5(() => {
820
+ const canvasEl = canvas();
821
+ const proj = projection();
822
+ const gen = generator();
823
+ const z = zoom();
824
+ const t = translation();
825
+ const r = rotation();
826
+ const s = size();
827
+ if (canvasEl && proj && gen) {
828
+ timer(() => {
829
+ proj.scale(Math.min(s.width, s.height) / 2 * z).translate([
830
+ s.width / 2 + (t?.x ?? 0),
831
+ s.height / 2 + (t?.y ?? 0)
832
+ ]).rotate(r ?? [
833
+ 0,
834
+ 0,
835
+ 0
836
+ ]);
837
+ renderLayers(gen, layers(), z, styles());
838
+ });
839
+ }
840
+ });
841
+ return /* @__PURE__ */ _jsx3(Show, {
842
+ when: size().width > 0 && size().height > 0,
843
+ children: /* @__PURE__ */ _jsx3("canvas", {
844
+ ref: setCanvas,
845
+ width: size().width,
846
+ height: size().height
847
+ })
848
+ });
849
+ };
850
+ var GlobeDebug = (props) => {
851
+ const { size, zoom, translation, rotation } = useGlobeContext();
852
+ return /* @__PURE__ */ _jsx3("div", {
853
+ class: `z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded ${controlPositions[props.position ?? "topleft"]}`,
854
+ children: /* @__PURE__ */ _jsx3("pre", {
855
+ class: "font-mono text-xs text-green-700",
856
+ children: JSON.stringify({
857
+ size,
858
+ zoom: zoom(),
859
+ translation: translation(),
860
+ rotation: rotation()
861
+ }, null, 2)
862
+ })
863
+ });
864
+ };
865
+ var GlobePanel = (props) => {
866
+ return /* @__PURE__ */ _jsx3("div", {
867
+ class: `z-10 absolute overflow-hidden ${controlPositions[props.position ?? "topleft"]} ${props.class ?? ""}`,
868
+ children: props.children
869
+ });
870
+ };
871
+ var CustomControl = (props) => {
872
+ return /* @__PURE__ */ _jsx3("div", {
873
+ class: `z-10 absolute overflow-hidden ${controlPositions[props.position]}`,
874
+ children: props.children
875
+ });
876
+ };
877
+ var GlobeZoom = (props) => /* @__PURE__ */ _jsx3(CustomControl, {
878
+ position: props.position ?? "bottomleft",
879
+ children: /* @__PURE__ */ _jsx3(ZoomControls, {
880
+ onAction: props.onAction
881
+ })
882
+ });
883
+ var GlobeAction = (props) => /* @__PURE__ */ _jsx3(CustomControl, {
884
+ position: props.position ?? "bottomright",
885
+ children: /* @__PURE__ */ _jsx3(ActionControls, {
886
+ onAction: props.onAction
887
+ })
888
+ });
889
+ var Globe = {
890
+ Root: GlobeRoot,
891
+ Canvas: GlobeCanvas,
892
+ Zoom: GlobeZoom,
893
+ Action: GlobeAction,
894
+ Debug: GlobeDebug,
895
+ Panel: GlobePanel
896
+ };
897
+
898
+ // src/components/Map/Map.tsx
899
+ import { jsx as _jsx4, jsxs as _jsxs2 } from "solid-js/jsx-runtime";
900
+ import "leaflet/dist/leaflet.css";
901
+ import L, { latLngBounds } from "leaflet";
902
+ import { Show as Show2, createContext as createContext2, createEffect as createEffect6, createSignal as createSignal5, onCleanup as onCleanup4, onMount, useContext as useContext2 } from "solid-js";
903
+ var defaults2 = {
904
+ center: {
905
+ lat: 51,
906
+ lng: 0
907
+ },
908
+ zoom: 4
909
+ };
910
+ var MapContext = createContext2();
911
+ var useMapContext = (displayName) => {
912
+ const context = useContext2(MapContext);
913
+ if (!context) {
914
+ throw new Error(`${displayName} must be used within Map.Root`);
915
+ }
916
+ return context;
917
+ };
918
+ var MapRoot = (props) => {
919
+ let mapContainer;
920
+ const [map, setMap] = createSignal5(null);
921
+ const [attention, setAttention] = createSignal5(false);
922
+ onMount(() => {
923
+ if (!mapContainer) return;
924
+ const leafletMap = L.map(mapContainer, {
925
+ center: props.center ?? defaults2.center,
926
+ zoom: props.zoom ?? defaults2.zoom,
927
+ attributionControl: false,
928
+ zoomControl: false,
929
+ scrollWheelZoom: props.scrollWheelZoom ?? true,
930
+ doubleClickZoom: props.doubleClickZoom ?? true,
931
+ touchZoom: props.touchZoom ?? true
932
+ });
933
+ setMap(leafletMap);
934
+ if (props.ref) {
935
+ props.ref({
936
+ setCenter: (center, zoom) => {
937
+ leafletMap.setView(center, zoom);
938
+ },
939
+ setZoom: (cb) => {
940
+ leafletMap.setZoom(cb(leafletMap.getZoom()));
941
+ }
942
+ });
943
+ }
944
+ onCleanup4(() => {
945
+ leafletMap.remove();
946
+ });
947
+ });
948
+ createEffect6(() => {
949
+ const leafletMap = map();
950
+ if (!leafletMap) return;
951
+ if (attention()) {
952
+ leafletMap.scrollWheelZoom.enable();
953
+ } else {
954
+ leafletMap.scrollWheelZoom.disable();
955
+ }
956
+ });
957
+ return /* @__PURE__ */ _jsxs2(MapContext.Provider, {
958
+ value: {
959
+ map,
960
+ attention,
961
+ setAttention,
962
+ onChange: props.onChange
963
+ },
964
+ children: [
965
+ /* @__PURE__ */ _jsx4("div", {
966
+ ref: mapContainer,
967
+ class: `group relative grid h-full w-full bg-gray-100 dark:bg-gray-900 ${props.class ?? ""}`,
968
+ style: {
969
+ "z-index": "0"
970
+ }
971
+ }),
972
+ /* @__PURE__ */ _jsx4(Show2, {
973
+ when: map(),
974
+ children: props.children
975
+ })
976
+ ]
977
+ });
978
+ };
979
+ var MapTiles = () => {
980
+ const { map, onChange, attention } = useMapContext(MapTiles.name);
981
+ let tileLayer = null;
982
+ createEffect6(() => {
983
+ const leafletMap = map();
984
+ if (!leafletMap) return;
985
+ const att = attention();
986
+ tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
987
+ detectRetina: true,
988
+ keepBuffer: 4,
989
+ className: `dark:grayscale dark:invert ${att ? "" : "opacity-80"}`
990
+ });
991
+ tileLayer.addTo(leafletMap);
992
+ leafletMap.on("zoomstart", (ev) => {
993
+ onChange?.({
994
+ center: ev.target.getCenter(),
995
+ zoom: ev.target.getZoom()
996
+ });
997
+ });
998
+ onCleanup4(() => {
999
+ if (tileLayer) {
1000
+ tileLayer.remove();
1001
+ tileLayer = null;
1002
+ }
1003
+ });
1004
+ });
1005
+ createEffect6(() => {
1006
+ const att = attention();
1007
+ if (tileLayer) {
1008
+ const container = tileLayer.getContainer();
1009
+ if (container) {
1010
+ container.className = `dark:grayscale dark:invert ${att ? "" : "opacity-80"}`;
1011
+ }
1012
+ }
1013
+ });
1014
+ return null;
1015
+ };
1016
+ var MapMarkers = (props) => {
1017
+ const { map } = useMapContext(MapMarkers.name);
1018
+ const leafletMarkers = [];
1019
+ let lastMarkerIds = /* @__PURE__ */ new Set();
1020
+ onCleanup4(() => {
1021
+ leafletMarkers.forEach((marker) => marker.remove());
1022
+ leafletMarkers.length = 0;
1023
+ });
1024
+ createEffect6(() => {
1025
+ const leafletMap = map();
1026
+ if (!leafletMap) return;
1027
+ const markerList = props.markers?.() ?? [];
1028
+ const currentIds = new Set(markerList.map((m) => m.id));
1029
+ if (currentIds.size === lastMarkerIds.size && [
1030
+ ...currentIds
1031
+ ].every((id) => lastMarkerIds.has(id)) && leafletMarkers.length > 0) {
1032
+ return;
1033
+ }
1034
+ leafletMarkers.forEach((marker) => marker.remove());
1035
+ leafletMarkers.length = 0;
1036
+ lastMarkerIds = currentIds;
1037
+ if (markerList.length > 0) {
1038
+ const bounds = latLngBounds(markerList.map((marker) => marker.location));
1039
+ leafletMap.fitBounds(bounds);
1040
+ markerList.forEach(({ title, location }) => {
1041
+ const marker = L.marker(location, {
1042
+ icon: new L.Icon({
1043
+ iconUrl: "https://dxos.network/marker-icon.png",
1044
+ iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1045
+ shadowUrl: "https://dxos.network/marker-shadow.png",
1046
+ iconSize: [
1047
+ 25,
1048
+ 41
1049
+ ],
1050
+ iconAnchor: [
1051
+ 12,
1052
+ 41
1053
+ ],
1054
+ popupAnchor: [
1055
+ 1,
1056
+ -34
1057
+ ],
1058
+ shadowSize: [
1059
+ 41,
1060
+ 41
1061
+ ]
1062
+ })
1063
+ });
1064
+ if (title) {
1065
+ marker.bindPopup(title);
1066
+ }
1067
+ marker.addTo(leafletMap);
1068
+ leafletMarkers.push(marker);
1069
+ });
1070
+ } else {
1071
+ leafletMap.setView(defaults2.center, defaults2.zoom);
1072
+ }
1073
+ });
1074
+ return null;
1075
+ };
1076
+ var CustomControl2 = (props) => {
1077
+ const { map: mapAccessor } = useMapContext(CustomControl2.name);
1078
+ let controlContainer;
1079
+ createEffect6(() => {
1080
+ const map = mapAccessor();
1081
+ if (!map || !controlContainer) return;
1082
+ const Control = L.Control.extend({
1083
+ onAdd: () => {
1084
+ const container = L.DomUtil.create("div", `${controlPositions[props.position]} !m-0`);
1085
+ L.DomEvent.disableClickPropagation(container);
1086
+ L.DomEvent.disableScrollPropagation(container);
1087
+ if (controlContainer) {
1088
+ container.appendChild(controlContainer);
1089
+ }
1090
+ return container;
1091
+ }
1092
+ });
1093
+ const control = new Control({
1094
+ position: props.position
1095
+ });
1096
+ control.addTo(map);
1097
+ onCleanup4(() => {
1098
+ control.remove();
1099
+ });
1100
+ });
1101
+ return /* @__PURE__ */ _jsx4("div", {
1102
+ ref: controlContainer,
1103
+ style: {
1104
+ display: "contents"
1105
+ },
1106
+ children: props.children
1107
+ });
1108
+ };
1109
+ var MapZoom = (props) => /* @__PURE__ */ _jsx4(CustomControl2, {
1110
+ position: props.position ?? "bottomleft",
1111
+ children: /* @__PURE__ */ _jsx4(ZoomControls, {
1112
+ onAction: props.onAction
1113
+ })
1114
+ });
1115
+ var MapAction = (props) => /* @__PURE__ */ _jsx4(CustomControl2, {
1116
+ position: props.position ?? "bottomright",
1117
+ children: /* @__PURE__ */ _jsx4(ActionControls, {
1118
+ onAction: props.onAction
1119
+ })
1120
+ });
1121
+ var Map = {
1122
+ Root: MapRoot,
1123
+ Tiles: MapTiles,
1124
+ Markers: MapMarkers,
1125
+ Zoom: MapZoom,
1126
+ Action: MapAction
1127
+ };
1128
+
1129
+ // src/translations.ts
1130
+ var translationKey = "@dxos/react-ui-geo";
1131
+ var translations = [
1132
+ {
1133
+ "en-US": {
1134
+ [translationKey]: {
1135
+ "zoom in icon button": "Zoom in",
1136
+ "zoom out icon button": "Zoom out",
1137
+ "start icon button": "Start",
1138
+ "toggle icon button": "Toggle"
1139
+ }
1140
+ }
1141
+ }
1142
+ ];
1143
+ export {
1144
+ ActionControls,
1145
+ Globe,
1146
+ GlobeContextProvider,
1147
+ Map,
1148
+ ZoomControls,
1149
+ closestPoint,
1150
+ controlPositions,
1151
+ createLayers,
1152
+ geoCircle,
1153
+ geoInertiaDrag,
1154
+ geoLine,
1155
+ geoPoint,
1156
+ geoToPosition,
1157
+ getDistance,
1158
+ loadTopology,
1159
+ positionToRotation,
1160
+ renderLayers,
1161
+ restrictAxis,
1162
+ timer,
1163
+ translationKey,
1164
+ translations,
1165
+ useDrag,
1166
+ useGlobeContext,
1167
+ useGlobeZoomHandler,
1168
+ useMapZoomHandler,
1169
+ useSpinner,
1170
+ useTour
1171
+ };
1172
+ //# sourceMappingURL=index.mjs.map