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