@dxos/react-ui-geo 0.8.3 → 0.8.4-main.1068cf700f

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 (86) hide show
  1. package/data/airports.ts +1 -1
  2. package/data/cities.ts +1 -1
  3. package/data/countries-110m.ts +1 -1
  4. package/data/countries-dots-3.ts +1 -1
  5. package/data/countries-dots-4.ts +1 -1
  6. package/dist/lib/browser/chunk-GMWLKTLN.mjs +9 -0
  7. package/dist/lib/browser/{countries-110m-WI4PCLDF.mjs → countries-110m-ZM3ZIEFS.mjs} +2 -2
  8. package/dist/lib/browser/countries-110m-ZM3ZIEFS.mjs.map +7 -0
  9. package/dist/lib/browser/data.mjs +1 -1
  10. package/dist/lib/browser/index.mjs +404 -458
  11. package/dist/lib/browser/index.mjs.map +4 -4
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/node-esm/{chunk-PIIEDZEU.mjs → chunk-JODBF4CC.mjs} +3 -3
  14. package/dist/lib/node-esm/{countries-110m-DQ4XRC4B.mjs → countries-110m-3SFASWVD.mjs} +2 -2
  15. package/dist/lib/node-esm/countries-110m-3SFASWVD.mjs.map +7 -0
  16. package/dist/lib/node-esm/data.mjs +1 -1
  17. package/dist/lib/node-esm/index.mjs +404 -458
  18. package/dist/lib/node-esm/index.mjs.map +4 -4
  19. package/dist/lib/node-esm/meta.json +1 -1
  20. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  21. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  22. package/dist/types/src/components/Globe/Globe.stories.d.ts +25 -9
  23. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/Map/Map.d.ts +28 -18
  25. package/dist/types/src/components/Map/Map.d.ts.map +1 -1
  26. package/dist/types/src/components/Map/Map.stories.d.ts +14 -8
  27. package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
  29. package/dist/types/src/components/index.d.ts +0 -1
  30. package/dist/types/src/components/index.d.ts.map +1 -1
  31. package/dist/types/src/hooks/context.d.ts +7 -7
  32. package/dist/types/src/hooks/context.d.ts.map +1 -1
  33. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +1 -1
  34. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -1
  35. package/dist/types/src/hooks/useMapZoomHandler.d.ts +1 -1
  36. package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -1
  37. package/dist/types/src/hooks/useSpinner.d.ts +1 -1
  38. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -1
  39. package/dist/types/src/hooks/useTour.d.ts +4 -3
  40. package/dist/types/src/hooks/useTour.d.ts.map +1 -1
  41. package/dist/types/src/index.d.ts +2 -1
  42. package/dist/types/src/index.d.ts.map +1 -1
  43. package/dist/types/src/translations.d.ts +12 -0
  44. package/dist/types/src/translations.d.ts.map +1 -0
  45. package/dist/types/src/types.d.ts +2 -1
  46. package/dist/types/src/types.d.ts.map +1 -1
  47. package/dist/types/src/util/path.d.ts +5 -8
  48. package/dist/types/src/util/path.d.ts.map +1 -1
  49. package/dist/types/src/util/render.d.ts +4 -4
  50. package/dist/types/src/util/render.d.ts.map +1 -1
  51. package/dist/types/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +29 -23
  53. package/src/components/Globe/Globe.stories.tsx +85 -37
  54. package/src/components/Globe/Globe.tsx +80 -63
  55. package/src/components/Map/Map.stories.tsx +25 -14
  56. package/src/components/Map/Map.tsx +183 -96
  57. package/src/components/Toolbar/Controls.tsx +14 -20
  58. package/src/components/index.ts +0 -2
  59. package/src/hooks/context.tsx +22 -16
  60. package/src/hooks/useGlobeZoomHandler.ts +9 -3
  61. package/src/hooks/useMapZoomHandler.ts +1 -1
  62. package/src/hooks/useSpinner.ts +2 -1
  63. package/src/hooks/useTour.ts +10 -8
  64. package/src/index.ts +2 -1
  65. package/src/translations.ts +20 -0
  66. package/src/types.ts +3 -1
  67. package/src/util/inertia.ts +1 -1
  68. package/src/util/path.ts +5 -6
  69. package/src/util/render.ts +5 -3
  70. package/dist/lib/browser/chunk-ENCWOTYX.mjs +0 -9
  71. package/dist/lib/browser/countries-110m-WI4PCLDF.mjs.map +0 -7
  72. package/dist/lib/node/chunk-LAICG6L2.cjs +0 -40
  73. package/dist/lib/node/chunk-LAICG6L2.cjs.map +0 -7
  74. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs +0 -37877
  75. package/dist/lib/node/countries-110m-KQ5WAB2O.cjs.map +0 -7
  76. package/dist/lib/node/data.cjs +0 -28
  77. package/dist/lib/node/data.cjs.map +0 -7
  78. package/dist/lib/node/index.cjs +0 -1187
  79. package/dist/lib/node/index.cjs.map +0 -7
  80. package/dist/lib/node/meta.json +0 -1
  81. package/dist/lib/node-esm/countries-110m-DQ4XRC4B.mjs.map +0 -7
  82. package/dist/types/src/components/types.d.ts +0 -15
  83. package/dist/types/src/components/types.d.ts.map +0 -1
  84. package/src/components/types.ts +0 -19
  85. /package/dist/lib/browser/{chunk-ENCWOTYX.mjs.map → chunk-GMWLKTLN.mjs.map} +0 -0
  86. /package/dist/lib/node-esm/{chunk-PIIEDZEU.mjs.map → chunk-JODBF4CC.mjs.map} +0 -0
@@ -1,55 +1,55 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
  import {
3
3
  loadTopology
4
- } from "./chunk-PIIEDZEU.mjs";
4
+ } from "./chunk-JODBF4CC.mjs";
5
5
 
6
- // packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
7
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
8
- import { geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition, easeLinear, easeSinOut } from "d3";
6
+ // src/components/Globe/Globe.tsx
7
+ import { easeLinear, easeSinOut, geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition } from "d3";
9
8
  import React3, { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useRef, useState as useState3 } from "react";
10
9
  import { useResizeDetector } from "react-resize-detector";
11
10
  import { useDynamicRef, useThemeContext } from "@dxos/react-ui";
12
- import { mx } from "@dxos/react-ui-theme";
11
+ import { mx } from "@dxos/ui-theme";
13
12
 
14
- // packages/ui/react-ui-geo/src/hooks/context.tsx
15
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
13
+ // src/hooks/context.tsx
16
14
  import React, { createContext, useContext } from "react";
17
15
  import { raise } from "@dxos/debug";
18
16
  import { useControlledState } from "@dxos/react-ui";
17
+ var defaults = {
18
+ center: {
19
+ lat: 51,
20
+ lng: 0
21
+ },
22
+ zoom: 4
23
+ };
19
24
  var GlobeContext = /* @__PURE__ */ createContext(void 0);
20
- var GlobeContextProvider = ({ children, size, center: _center, scale: _scale, translation: _translation, rotation: _rotation }) => {
21
- var _effect = _useSignals();
22
- try {
23
- const [center, setCenter] = useControlledState(_center);
24
- const [scale, setScale] = useControlledState(_scale);
25
- const [translation, setTranslation] = useControlledState(_translation);
26
- const [rotation, setRotation] = useControlledState(_rotation);
27
- return /* @__PURE__ */ React.createElement(GlobeContext.Provider, {
28
- value: {
29
- size,
30
- center,
31
- scale,
32
- translation,
33
- rotation,
34
- setCenter,
35
- setScale,
36
- setTranslation,
37
- setRotation
38
- }
39
- }, children);
40
- } finally {
41
- _effect.f();
42
- }
25
+ var GlobeContextProvider = ({ children, size, center: centerProp = defaults.center, zoom: zoomProp = defaults.zoom, translation: translationProp, rotation: rotationProp }) => {
26
+ const [center, setCenter] = useControlledState(centerProp);
27
+ const [zoom, setZoom] = useControlledState(zoomProp);
28
+ const [translation, setTranslation] = useControlledState(translationProp);
29
+ const [rotation, setRotation] = useControlledState(rotationProp);
30
+ return /* @__PURE__ */ React.createElement(GlobeContext.Provider, {
31
+ value: {
32
+ size,
33
+ center,
34
+ zoom,
35
+ translation,
36
+ rotation,
37
+ setCenter,
38
+ setZoom,
39
+ setTranslation,
40
+ setRotation
41
+ }
42
+ }, children);
43
43
  };
44
44
  var useGlobeContext = () => {
45
45
  return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
46
46
  };
47
47
 
48
- // packages/ui/react-ui-geo/src/hooks/useDrag.ts
48
+ // src/hooks/useDrag.ts
49
49
  import { select as select2 } from "d3";
50
50
  import { useEffect } from "react";
51
51
 
52
- // packages/ui/react-ui-geo/src/util/debug.ts
52
+ // src/util/debug.ts
53
53
  var debug = false;
54
54
  var timer = (cb) => {
55
55
  const start = Date.now();
@@ -64,8 +64,8 @@ var timer = (cb) => {
64
64
  return data;
65
65
  };
66
66
 
67
- // packages/ui/react-ui-geo/src/util/inertia.ts
68
- import { select, drag, timer as timer2 } from "d3";
67
+ // src/util/inertia.ts
68
+ import { drag, select, timer as timer2 } from "d3";
69
69
  import versor from "versor";
70
70
  var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
71
71
  var geoInertiaDrag = (target, render, projection, options) => {
@@ -239,7 +239,7 @@ function inertiaHelper(opt) {
239
239
  return inertia;
240
240
  }
241
241
 
242
- // packages/ui/react-ui-geo/src/util/path.ts
242
+ // src/util/path.ts
243
243
  import { geoCircle as d3GeoCircle } from "d3";
244
244
  var positionToRotation = ([lng, lat], tilt = 0) => [
245
245
  -lng,
@@ -292,7 +292,7 @@ var getDistance = (point1, point2) => {
292
292
  return Math.sqrt(dx * dx + dy * dy);
293
293
  };
294
294
 
295
- // packages/ui/react-ui-geo/src/util/render.ts
295
+ // src/util/render.ts
296
296
  import { geoGraticule } from "d3";
297
297
  import { feature, mesh } from "topojson-client";
298
298
  var createLayers = (topology, features, styles) => {
@@ -391,7 +391,7 @@ var renderLayers = (generator, layers = [], scale, styles) => {
391
391
  return context;
392
392
  };
393
393
 
394
- // packages/ui/react-ui-geo/src/hooks/useDrag.ts
394
+ // src/hooks/useDrag.ts
395
395
  var useDrag = (controller, options = {}) => {
396
396
  useEffect(() => {
397
397
  const canvas = controller?.canvas;
@@ -426,8 +426,9 @@ var useDrag = (controller, options = {}) => {
426
426
  };
427
427
  var cancelDrag = (node) => node.on(".drag", null);
428
428
 
429
- // packages/ui/react-ui-geo/src/hooks/useGlobeZoomHandler.ts
429
+ // src/hooks/useGlobeZoomHandler.ts
430
430
  import { useCallback } from "react";
431
+ var ZOOM_FACTOR = 0.1;
431
432
  var useGlobeZoomHandler = (controller) => {
432
433
  return useCallback((event) => {
433
434
  if (!controller) {
@@ -435,11 +436,15 @@ var useGlobeZoomHandler = (controller) => {
435
436
  }
436
437
  switch (event) {
437
438
  case "zoom-in": {
438
- controller.setScale((scale) => scale * 1.1);
439
+ controller.setZoom((zoom) => {
440
+ return zoom * (1 + ZOOM_FACTOR);
441
+ });
439
442
  break;
440
443
  }
441
444
  case "zoom-out": {
442
- controller.setScale((scale) => scale * 0.9);
445
+ controller.setZoom((zoom) => {
446
+ return zoom * (1 - ZOOM_FACTOR);
447
+ });
443
448
  break;
444
449
  }
445
450
  }
@@ -448,7 +453,7 @@ var useGlobeZoomHandler = (controller) => {
448
453
  ]);
449
454
  };
450
455
 
451
- // packages/ui/react-ui-geo/src/hooks/useMapZoomHandler.ts
456
+ // src/hooks/useMapZoomHandler.ts
452
457
  import { useCallback as useCallback2 } from "react";
453
458
  var useMapZoomHandler = (controller) => {
454
459
  return useCallback2((event) => {
@@ -470,7 +475,7 @@ var useMapZoomHandler = (controller) => {
470
475
  ]);
471
476
  };
472
477
 
473
- // packages/ui/react-ui-geo/src/hooks/useSpinner.ts
478
+ // src/hooks/useSpinner.ts
474
479
  import { timer as d3Timer } from "d3";
475
480
  import { useEffect as useEffect2, useState } from "react";
476
481
  var useSpinner = (controller, options = {}) => {
@@ -523,9 +528,9 @@ var useSpinner = (controller, options = {}) => {
523
528
  ];
524
529
  };
525
530
 
526
- // packages/ui/react-ui-geo/src/hooks/useTour.ts
527
- import { geoPath, geoInterpolate, geoDistance, selection as d3Selection } from "d3";
528
- import { useEffect as useEffect3, useState as useState2, useMemo } from "react";
531
+ // src/hooks/useTour.ts
532
+ import { selection as d3Selection, geoDistance, geoInterpolate, geoPath } from "d3";
533
+ import { useEffect as useEffect3, useMemo, useState as useState2 } from "react";
529
534
  import versor2 from "versor";
530
535
  var TRANSITION_NAME = "globe-tour";
531
536
  var defaultDuration = 1500;
@@ -572,7 +577,7 @@ var useTour = (controller, points, options = {}) => {
572
577
  {
573
578
  context.beginPath();
574
579
  context.strokeStyle = options?.styles?.arc?.strokeStyle ?? "yellow";
575
- context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.scale ?? 1);
580
+ context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.zoom ?? 1);
576
581
  context.setLineDash(options?.styles?.arc?.lineDash ?? []);
577
582
  path({
578
583
  type: "LineString",
@@ -584,7 +589,7 @@ var useTour = (controller, points, options = {}) => {
584
589
  context.stroke();
585
590
  context.beginPath();
586
591
  context.fillStyle = options?.styles?.cursor?.fillStyle ?? "orange";
587
- path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.scale ?? 1));
592
+ path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.zoom ?? 1));
588
593
  path({
589
594
  type: "Point",
590
595
  coordinates: ip(t22)
@@ -598,7 +603,7 @@ var useTour = (controller, points, options = {}) => {
598
603
  await transition2.end();
599
604
  last = next;
600
605
  }
601
- } catch (err) {
606
+ } catch {
602
607
  } finally {
603
608
  setRunning(false);
604
609
  }
@@ -619,10 +624,26 @@ var useTour = (controller, points, options = {}) => {
619
624
  ];
620
625
  };
621
626
 
622
- // packages/ui/react-ui-geo/src/components/Toolbar/Controls.tsx
623
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
627
+ // src/components/Toolbar/Controls.tsx
624
628
  import React2 from "react";
625
- import { IconButton, Toolbar } from "@dxos/react-ui";
629
+ import { IconButton, Toolbar, useTranslation } from "@dxos/react-ui";
630
+
631
+ // src/translations.ts
632
+ var translationKey = "@dxos/react-ui-geo";
633
+ var translations = [
634
+ {
635
+ "en-US": {
636
+ [translationKey]: {
637
+ "zoom in icon button": "Zoom in",
638
+ "zoom out icon button": "Zoom out",
639
+ "start icon button": "Start",
640
+ "toggle icon button": "Toggle"
641
+ }
642
+ }
643
+ }
644
+ ];
645
+
646
+ // src/components/Toolbar/Controls.tsx
626
647
  var controlPositions = {
627
648
  topleft: "top-2 left-2",
628
649
  topright: "top-2 right-2",
@@ -630,65 +651,45 @@ var controlPositions = {
630
651
  bottomright: "bottom-2 right-2"
631
652
  };
632
653
  var ZoomControls = ({ classNames, onAction }) => {
633
- var _effect = _useSignals2();
634
- try {
635
- return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
636
- classNames: [
637
- "gap-1",
638
- classNames
639
- ]
640
- }, /* @__PURE__ */ React2.createElement(IconButton, {
641
- //
642
- icon: "ph--plus--regular",
643
- label: "zoom in",
644
- iconOnly: true,
645
- size: 5,
646
- classNames: "px-0 aspect-square",
647
- onClick: () => onAction?.("zoom-in")
648
- }), /* @__PURE__ */ React2.createElement(IconButton, {
649
- //
650
- icon: "ph--minus--regular",
651
- label: "zoom out",
652
- iconOnly: true,
653
- size: 5,
654
- classNames: "px-0 aspect-square",
655
- onClick: () => onAction?.("zoom-out")
656
- }));
657
- } finally {
658
- _effect.f();
659
- }
654
+ const { t } = useTranslation(translationKey);
655
+ return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
656
+ classNames: [
657
+ "gap-2",
658
+ classNames
659
+ ]
660
+ }, /* @__PURE__ */ React2.createElement(IconButton, {
661
+ icon: "ph--plus--regular",
662
+ iconOnly: true,
663
+ label: t("zoom in icon button"),
664
+ onClick: () => onAction?.("zoom-in")
665
+ }), /* @__PURE__ */ React2.createElement(IconButton, {
666
+ icon: "ph--minus--regular",
667
+ iconOnly: true,
668
+ label: t("zoom out icon button"),
669
+ onClick: () => onAction?.("zoom-out")
670
+ }));
660
671
  };
661
672
  var ActionControls = ({ classNames, onAction }) => {
662
- var _effect = _useSignals2();
663
- try {
664
- return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
665
- classNames: [
666
- "gap-1",
667
- classNames
668
- ]
669
- }, /* @__PURE__ */ React2.createElement(IconButton, {
670
- //
671
- icon: "ph--play--regular",
672
- label: "start",
673
- iconOnly: true,
674
- size: 5,
675
- classNames: "px-0 aspect-square",
676
- onClick: () => onAction?.("start")
677
- }), /* @__PURE__ */ React2.createElement(IconButton, {
678
- //
679
- icon: "ph--globe-hemisphere-west--regular",
680
- label: "toggle",
681
- iconOnly: true,
682
- size: 5,
683
- classNames: "px-0 aspect-square",
684
- onClick: () => onAction?.("toggle")
685
- }));
686
- } finally {
687
- _effect.f();
688
- }
673
+ const { t } = useTranslation(translationKey);
674
+ return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
675
+ classNames: [
676
+ "gap-2",
677
+ classNames
678
+ ]
679
+ }, /* @__PURE__ */ React2.createElement(IconButton, {
680
+ icon: "ph--path--regular",
681
+ iconOnly: true,
682
+ label: t("start icon button"),
683
+ onClick: () => onAction?.("start")
684
+ }), /* @__PURE__ */ React2.createElement(IconButton, {
685
+ icon: "ph--globe-hemisphere-west--regular",
686
+ iconOnly: true,
687
+ label: t("toggle icon button"),
688
+ onClick: () => onAction?.("toggle")
689
+ }));
689
690
  };
690
691
 
691
- // packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
692
+ // src/components/Globe/Globe.tsx
692
693
  var defaultStyles = {
693
694
  light: {
694
695
  background: {
@@ -744,394 +745,337 @@ var getProjection = (type = "orthographic") => {
744
745
  return type ?? geoOrthographic();
745
746
  };
746
747
  var GlobeRoot = ({ classNames, children, ...props }) => {
747
- var _effect = _useSignals3();
748
- try {
749
- const { ref, width, height } = useResizeDetector();
750
- return /* @__PURE__ */ React3.createElement("div", {
751
- ref,
752
- className: mx("relative flex grow overflow-hidden", classNames)
753
- }, /* @__PURE__ */ React3.createElement(GlobeContextProvider, {
754
- size: {
755
- width,
756
- height
757
- },
758
- ...props
759
- }, children));
760
- } finally {
761
- _effect.f();
762
- }
748
+ const { ref, width, height } = useResizeDetector();
749
+ return /* @__PURE__ */ React3.createElement("div", {
750
+ ref,
751
+ className: mx("relative flex grow overflow-hidden", classNames)
752
+ }, /* @__PURE__ */ React3.createElement(GlobeContextProvider, {
753
+ size: {
754
+ width,
755
+ height
756
+ },
757
+ ...props
758
+ }, children));
763
759
  };
764
- var GlobeCanvas = /* @__PURE__ */ forwardRef(({ projection: _projection, topology, features, styles: _styles }, forwardRef3) => {
765
- var _effect = _useSignals3();
766
- try {
767
- const { themeMode } = useThemeContext();
768
- const styles = useMemo2(() => _styles ?? defaultStyles[themeMode], [
769
- _styles,
770
- themeMode
771
- ]);
772
- const [canvas, setCanvas] = useState3(null);
773
- const canvasRef = (canvas2) => setCanvas(canvas2);
774
- const projection = useMemo2(() => getProjection(_projection), [
775
- _projection
776
- ]);
777
- const layers = useMemo2(() => {
778
- return timer(() => createLayers(topology, features, styles));
779
- }, [
780
- topology,
781
- features,
782
- styles
783
- ]);
784
- const { size, center, scale, translation, rotation, setCenter, setScale, setTranslation, setRotation } = useGlobeContext();
785
- const scaleRef = useDynamicRef(scale);
786
- useEffect4(() => {
787
- if (center) {
788
- setScale(1);
789
- setRotation(positionToRotation(geoToPosition(center)));
790
- }
791
- }, [
792
- center
793
- ]);
794
- const zooming = useRef(false);
795
- useImperativeHandle(forwardRef3, () => {
796
- return {
797
- canvas,
798
- projection,
799
- center,
800
- get scale() {
801
- return scaleRef.current;
802
- },
803
- translation,
804
- rotation,
805
- setCenter,
806
- setScale: (s) => {
807
- if (typeof s === "function") {
808
- const is = interpolateNumber(scaleRef.current, s(scaleRef.current));
809
- transition().ease(zooming.current ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => setScale(is(t))).on("end", () => {
810
- zooming.current = false;
811
- });
812
- } else {
813
- setScale(s);
814
- }
815
- },
816
- setTranslation,
817
- setRotation
818
- };
819
- }, [
820
- canvas
821
- ]);
822
- const generator = useMemo2(() => canvas && projection && geoPath2(projection, canvas.getContext("2d", {
823
- alpha: false
824
- })), [
760
+ var GlobeCanvas = /* @__PURE__ */ forwardRef(({ projection: projectionProp, topology, features, styles: stylesProp }, forwardRef3) => {
761
+ const { themeMode } = useThemeContext();
762
+ const styles = useMemo2(() => stylesProp ?? defaultStyles[themeMode], [
763
+ stylesProp,
764
+ themeMode
765
+ ]);
766
+ const [canvas, setCanvas] = useState3(null);
767
+ const canvasRef = (canvas2) => setCanvas(canvas2);
768
+ const projection = useMemo2(() => getProjection(projectionProp), [
769
+ projectionProp
770
+ ]);
771
+ const layers = useMemo2(() => {
772
+ return timer(() => createLayers(topology, features, styles));
773
+ }, [
774
+ topology,
775
+ features,
776
+ styles
777
+ ]);
778
+ const { size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation } = useGlobeContext();
779
+ const zoomRef = useDynamicRef(zoom);
780
+ useEffect4(() => {
781
+ if (center) {
782
+ setZoom(1);
783
+ setRotation(positionToRotation(geoToPosition(center)));
784
+ }
785
+ }, [
786
+ center
787
+ ]);
788
+ const zooming = useRef(false);
789
+ useImperativeHandle(forwardRef3, () => {
790
+ return {
825
791
  canvas,
826
- projection
827
- ]);
828
- useEffect4(() => {
829
- if (canvas && projection) {
830
- timer(() => {
831
- projection.scale(Math.min(size.width, size.height) / 2 * scale).translate([
832
- size.width / 2 + (translation?.x ?? 0),
833
- size.height / 2 + (translation?.y ?? 0)
834
- ]).rotate(rotation ?? [
835
- 0,
836
- 0,
837
- 0
838
- ]);
839
- renderLayers(generator, layers, scale, styles);
840
- });
841
- }
842
- }, [
843
- generator,
844
- size,
845
- scale,
792
+ projection,
793
+ center,
794
+ get zoom() {
795
+ return zoomRef.current;
796
+ },
846
797
  translation,
847
798
  rotation,
848
- layers
849
- ]);
850
- if (!size.width || !size.height) {
851
- return null;
799
+ setCenter,
800
+ setZoom: (state) => {
801
+ if (typeof state === "function") {
802
+ const is = interpolateNumber(zoomRef.current, state(zoomRef.current));
803
+ transition().ease(zooming.current ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => setZoom(is(t))).on("end", () => {
804
+ zooming.current = false;
805
+ });
806
+ } else {
807
+ setZoom(state);
808
+ }
809
+ },
810
+ setTranslation,
811
+ setRotation
812
+ };
813
+ }, [
814
+ canvas
815
+ ]);
816
+ const generator = useMemo2(() => canvas && projection && geoPath2(projection, canvas.getContext("2d", {
817
+ alpha: false
818
+ })), [
819
+ canvas,
820
+ projection
821
+ ]);
822
+ useEffect4(() => {
823
+ if (canvas && projection) {
824
+ timer(() => {
825
+ projection.scale(Math.min(size.width, size.height) / 2 * zoom).translate([
826
+ size.width / 2 + (translation?.x ?? 0),
827
+ size.height / 2 + (translation?.y ?? 0)
828
+ ]).rotate(rotation ?? [
829
+ 0,
830
+ 0,
831
+ 0
832
+ ]);
833
+ renderLayers(generator, layers, zoom, styles);
834
+ });
852
835
  }
853
- return /* @__PURE__ */ React3.createElement("canvas", {
854
- ref: canvasRef,
855
- width: size.width,
856
- height: size.height
857
- });
858
- } finally {
859
- _effect.f();
836
+ }, [
837
+ generator,
838
+ size,
839
+ zoom,
840
+ translation,
841
+ rotation,
842
+ layers
843
+ ]);
844
+ if (!size.width || !size.height) {
845
+ return null;
860
846
  }
847
+ return /* @__PURE__ */ React3.createElement("canvas", {
848
+ ref: canvasRef,
849
+ width: size.width,
850
+ height: size.height
851
+ });
861
852
  });
862
853
  var GlobeDebug = ({ position = "topleft" }) => {
863
- var _effect = _useSignals3();
864
- try {
865
- const { size, scale, translation, rotation } = useGlobeContext();
866
- return /* @__PURE__ */ React3.createElement("div", {
867
- className: mx("z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded", controlPositions[position])
868
- }, /* @__PURE__ */ React3.createElement("pre", {
869
- className: "font-mono text-xs text-green-700"
870
- }, JSON.stringify({
871
- size,
872
- scale,
873
- translation,
874
- rotation
875
- }, null, 2)));
876
- } finally {
877
- _effect.f();
878
- }
854
+ const { size, zoom, translation, rotation } = useGlobeContext();
855
+ return /* @__PURE__ */ React3.createElement("div", {
856
+ className: mx("z-10 absolute is-96 p-2 overflow-hidden border border-green-700 rounded", controlPositions[position])
857
+ }, /* @__PURE__ */ React3.createElement("pre", {
858
+ className: "font-mono text-xs text-green-700"
859
+ }, JSON.stringify({
860
+ size,
861
+ zoom,
862
+ translation,
863
+ rotation
864
+ }, null, 2)));
879
865
  };
880
866
  var GlobePanel = ({ position, classNames, children }) => {
881
- var _effect = _useSignals3();
882
- try {
883
- return /* @__PURE__ */ React3.createElement("div", {
884
- className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
885
- }, children);
886
- } finally {
887
- _effect.f();
888
- }
867
+ return /* @__PURE__ */ React3.createElement("div", {
868
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
869
+ }, children);
889
870
  };
890
871
  var CustomControl = ({ position, children }) => {
891
- var _effect = _useSignals3();
892
- try {
893
- return /* @__PURE__ */ React3.createElement("div", {
894
- className: mx("z-10 absolute overflow-hidden", controlPositions[position])
895
- }, children);
896
- } finally {
897
- _effect.f();
898
- }
872
+ return /* @__PURE__ */ React3.createElement("div", {
873
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position])
874
+ }, children);
899
875
  };
876
+ var GlobeZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl, {
877
+ position,
878
+ ...props
879
+ }, /* @__PURE__ */ React3.createElement(ZoomControls, {
880
+ onAction
881
+ }));
882
+ var GlobeAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl, {
883
+ position,
884
+ ...props
885
+ }, /* @__PURE__ */ React3.createElement(ActionControls, {
886
+ onAction
887
+ }));
900
888
  var Globe = {
901
889
  Root: GlobeRoot,
902
890
  Canvas: GlobeCanvas,
903
- Zoom: ({ onAction, position = "bottomleft", ...props }) => {
904
- var _effect = _useSignals3();
905
- try {
906
- return /* @__PURE__ */ React3.createElement(CustomControl, {
907
- position,
908
- ...props
909
- }, /* @__PURE__ */ React3.createElement(ZoomControls, {
910
- onAction
911
- }));
912
- } finally {
913
- _effect.f();
914
- }
915
- },
916
- Action: ({ onAction, position = "bottomright", ...props }) => {
917
- var _effect = _useSignals3();
918
- try {
919
- return /* @__PURE__ */ React3.createElement(CustomControl, {
920
- position,
921
- ...props
922
- }, /* @__PURE__ */ React3.createElement(ActionControls, {
923
- onAction
924
- }));
925
- } finally {
926
- _effect.f();
927
- }
928
- },
891
+ Zoom: GlobeZoom,
892
+ Action: GlobeAction,
929
893
  Debug: GlobeDebug,
930
894
  Panel: GlobePanel
931
895
  };
932
896
 
933
- // packages/ui/react-ui-geo/src/components/Map/Map.tsx
934
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
897
+ // src/components/Map/Map.tsx
935
898
  import "leaflet/dist/leaflet.css";
899
+ import { createContext as createContext2 } from "@radix-ui/react-context";
936
900
  import L, { Control, DomEvent, DomUtil, latLngBounds } from "leaflet";
937
- import React4, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2 } from "react";
901
+ import React4, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useRef as useRef2, useState as useState4 } from "react";
938
902
  import { createRoot } from "react-dom/client";
939
- import { MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet";
940
- import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
941
- import { debounce } from "@dxos/async";
903
+ import { MapContainer, Marker, Popup, TileLayer, useMap, useMapEvents } from "react-leaflet";
942
904
  import { ThemeProvider, Tooltip } from "@dxos/react-ui";
943
- import { defaultTx, mx as mx2 } from "@dxos/react-ui-theme";
944
- var defaults = {
945
- // TODO(burdon): Guess location.
905
+ import { defaultTx, mx as mx2 } from "@dxos/ui-theme";
906
+ var defaults2 = {
946
907
  center: {
947
908
  lat: 51,
948
909
  lng: 0
949
910
  },
950
911
  zoom: 4
951
912
  };
952
- var MapRoot = ({ classNames, center = defaults.center, zoom = defaults.zoom, ...props }) => {
953
- var _effect = _useSignals4();
954
- try {
955
- return /* @__PURE__ */ React4.createElement(MapContainer, {
956
- className: mx2("relative grid grow bg-baseSurface", classNames),
957
- attributionControl: false,
958
- // TODO(burdon): Only if attention.
959
- scrollWheelZoom: true,
960
- zoomControl: false,
961
- center,
962
- zoom,
963
- ...props
964
- });
965
- } finally {
966
- _effect.f();
967
- }
913
+ var [MapContextProvier, useMapContext] = createContext2("Map");
914
+ var MapRoot = /* @__PURE__ */ forwardRef2(({ classNames, scrollWheelZoom = true, doubleClickZoom = true, touchZoom = true, center, zoom, onChange, ...props }, forwardedRef) => {
915
+ const [attention, setAttention] = useState4(false);
916
+ const mapRef = useRef2(null);
917
+ const map = mapRef.current;
918
+ useImperativeHandle2(forwardedRef, () => ({
919
+ setCenter: (center2, zoom2) => {
920
+ mapRef.current?.setView(center2, zoom2);
921
+ },
922
+ setZoom: (cb) => {
923
+ mapRef.current?.setZoom(cb(mapRef.current?.getZoom() ?? 0));
924
+ }
925
+ }), []);
926
+ useEffect5(() => {
927
+ if (!map) {
928
+ return;
929
+ }
930
+ if (attention) {
931
+ map.scrollWheelZoom.enable();
932
+ } else {
933
+ map.scrollWheelZoom.disable();
934
+ }
935
+ }, [
936
+ map,
937
+ attention
938
+ ]);
939
+ return /* @__PURE__ */ React4.createElement(MapContextProvier, {
940
+ attention,
941
+ onChange
942
+ }, /* @__PURE__ */ React4.createElement(MapContainer, {
943
+ ...props,
944
+ ref: mapRef,
945
+ className: mx2("group relative grid bs-full is-full !bg-baseSurface dx-focus-ring-inset", classNames),
946
+ attributionControl: false,
947
+ zoomControl: false,
948
+ scrollWheelZoom,
949
+ doubleClickZoom,
950
+ touchZoom,
951
+ center: center ?? defaults2.center,
952
+ zoom: zoom ?? defaults2.zoom
953
+ }));
954
+ });
955
+ MapRoot.displayName = "Map.Root";
956
+ var MAP_TILES_NAME = "Map.Tiles";
957
+ var MapTiles = (_props) => {
958
+ const ref = useRef2(null);
959
+ const { onChange } = useMapContext(MAP_TILES_NAME);
960
+ useMapEvents({
961
+ zoomstart: (ev) => {
962
+ onChange?.({
963
+ center: ev.target.getCenter(),
964
+ zoom: ev.target.getZoom()
965
+ });
966
+ }
967
+ });
968
+ const { attention } = useMapContext(MAP_TILES_NAME);
969
+ useEffect5(() => {
970
+ if (ref.current) {
971
+ ref.current.getContainer().dataset.attention = attention ? "1" : "0";
972
+ }
973
+ }, [
974
+ attention
975
+ ]);
976
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(TileLayer, {
977
+ ref,
978
+ "data-attention": attention,
979
+ detectRetina: true,
980
+ className: 'dark:grayscale dark:invert data-[attention="0"]:!opacity-80',
981
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
982
+ keepBuffer: 4
983
+ }));
968
984
  };
969
- var MapCanvas = /* @__PURE__ */ forwardRef2(({ markers, center, zoom, onChange }, forwardedRef) => {
970
- var _effect = _useSignals4();
971
- try {
972
- const { ref, width, height } = useResizeDetector2({
973
- refreshRate: 200
974
- });
975
- const map = useMap();
976
- useImperativeHandle2(forwardedRef, () => ({
977
- setCenter: (center2, zoom2) => {
978
- map.setView(center2, zoom2);
985
+ MapTiles.displayName = MAP_TILES_NAME;
986
+ var MapMarkers = ({ selected, markers }) => {
987
+ const map = useMap();
988
+ useEffect5(() => {
989
+ if (markers.length > 0) {
990
+ const bounds = latLngBounds(markers.map((marker) => marker.location));
991
+ map.fitBounds(bounds);
992
+ } else {
993
+ map.setView(defaults2.center, defaults2.zoom);
994
+ }
995
+ }, [
996
+ markers
997
+ ]);
998
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, markers?.map(({ id, title, location: { lat, lng } }) => {
999
+ return /* @__PURE__ */ React4.createElement(Marker, {
1000
+ key: id,
1001
+ position: {
1002
+ lat,
1003
+ lng
979
1004
  },
980
- setZoom: (cb) => {
981
- map.setZoom(cb(map.getZoom()));
982
- }
983
- }), [
984
- map
985
- ]);
986
- useEffect5(() => {
987
- if (width && height) {
988
- map.invalidateSize();
989
- }
990
- }, [
991
- width,
992
- height
993
- ]);
994
- useEffect5(() => {
995
- if (center) {
996
- map.setView(center, zoom);
997
- } else if (zoom !== void 0) {
998
- map.setZoom(zoom);
999
- }
1000
- }, [
1001
- center,
1002
- zoom
1003
- ]);
1004
- useEffect5(() => {
1005
- const handler = debounce(() => {
1006
- onChange?.({
1007
- center: map.getCenter(),
1008
- zoom: map.getZoom()
1009
- });
1010
- }, 100);
1011
- map.on("move", handler);
1012
- map.on("zoom", handler);
1013
- return () => {
1014
- map.off("move", handler);
1015
- map.off("zoom", handler);
1016
- };
1017
- }, [
1018
- map,
1019
- onChange
1020
- ]);
1021
- useEffect5(() => {
1022
- if (markers.length > 0) {
1023
- const bounds = latLngBounds(markers.map((marker) => marker.location));
1024
- map.fitBounds(bounds);
1025
- } else {
1026
- map.setView(defaults.center, defaults.zoom);
1027
- }
1028
- }, [
1029
- markers
1030
- ]);
1031
- return /* @__PURE__ */ React4.createElement("div", {
1032
- ref,
1033
- className: "flex w-full h-full overflow-hidden bg-baseSurface"
1034
- }, /* @__PURE__ */ React4.createElement(TileLayer, {
1035
- className: "dark:filter dark:grayscale dark:invert",
1036
- url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
1037
- }), markers?.map(({ id, title, location: { lat, lng } }) => {
1038
- return /* @__PURE__ */ React4.createElement(Marker, {
1039
- key: id,
1040
- position: {
1041
- lat,
1042
- lng
1043
- },
1044
- icon: (
1045
- // TODO(burdon): Create custom icon from bundled assets.
1046
- new L.Icon({
1047
- iconUrl: "https://dxos.network/marker-icon.png",
1048
- iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1049
- shadowUrl: "https://dxos.network/marker-shadow.png",
1050
- iconSize: [
1051
- 25,
1052
- 41
1053
- ],
1054
- iconAnchor: [
1055
- 12,
1056
- 41
1057
- ],
1058
- popupAnchor: [
1059
- 1,
1060
- -34
1061
- ],
1062
- shadowSize: [
1063
- 41,
1064
- 41
1065
- ]
1066
- })
1067
- )
1068
- }, title && /* @__PURE__ */ React4.createElement(Popup, null, title));
1069
- }));
1070
- } finally {
1071
- _effect.f();
1072
- }
1073
- });
1005
+ icon: (
1006
+ // TODO(burdon): Create custom icon from bundled assets.
1007
+ // TODO(burdon): Selection state.
1008
+ new L.Icon({
1009
+ iconUrl: "https://dxos.network/marker-icon.png",
1010
+ iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1011
+ shadowUrl: "https://dxos.network/marker-shadow.png",
1012
+ iconSize: [
1013
+ 25,
1014
+ 41
1015
+ ],
1016
+ iconAnchor: [
1017
+ 12,
1018
+ 41
1019
+ ],
1020
+ popupAnchor: [
1021
+ 1,
1022
+ -34
1023
+ ],
1024
+ shadowSize: [
1025
+ 41,
1026
+ 41
1027
+ ]
1028
+ })
1029
+ )
1030
+ }, title && /* @__PURE__ */ React4.createElement(Popup, null, title));
1031
+ }));
1032
+ };
1033
+ MapMarkers.displayName = "Map.Markers";
1074
1034
  var CustomControl2 = ({ position, children }) => {
1075
- var _effect = _useSignals4();
1076
- try {
1077
- const map = useMap();
1078
- useEffect5(() => {
1079
- const control = new Control({
1080
- position
1081
- });
1082
- control.onAdd = () => {
1083
- const container = DomUtil.create("div", mx2("!m-0", controlPositions[position]));
1084
- DomEvent.disableClickPropagation(container);
1085
- DomEvent.disableScrollPropagation(container);
1086
- const root = createRoot(container);
1087
- root.render(/* @__PURE__ */ React4.createElement(ThemeProvider, {
1088
- tx: defaultTx
1089
- }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, children)));
1090
- return container;
1091
- };
1092
- control.addTo(map);
1093
- return () => {
1094
- control.remove();
1095
- };
1096
- }, [
1097
- map,
1098
- position,
1099
- children
1100
- ]);
1101
- return null;
1102
- } finally {
1103
- _effect.f();
1104
- }
1035
+ const map = useMap();
1036
+ useEffect5(() => {
1037
+ const control = new Control({
1038
+ position
1039
+ });
1040
+ control.onAdd = () => {
1041
+ const container = DomUtil.create("div", mx2("!m-0", controlPositions[position]));
1042
+ DomEvent.disableClickPropagation(container);
1043
+ DomEvent.disableScrollPropagation(container);
1044
+ const root = createRoot(container);
1045
+ root.render(/* @__PURE__ */ React4.createElement(ThemeProvider, {
1046
+ tx: defaultTx
1047
+ }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, children)));
1048
+ return container;
1049
+ };
1050
+ control.addTo(map);
1051
+ return () => {
1052
+ control.remove();
1053
+ };
1054
+ }, [
1055
+ map,
1056
+ position,
1057
+ children
1058
+ ]);
1059
+ return null;
1105
1060
  };
1061
+ var MapZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
1062
+ position,
1063
+ ...props
1064
+ }, /* @__PURE__ */ React4.createElement(ZoomControls, {
1065
+ onAction
1066
+ }));
1067
+ var MapAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
1068
+ position,
1069
+ ...props
1070
+ }, /* @__PURE__ */ React4.createElement(ActionControls, {
1071
+ onAction
1072
+ }));
1106
1073
  var Map = {
1107
1074
  Root: MapRoot,
1108
- Canvas: MapCanvas,
1109
- Zoom: ({ onAction, position = "bottomleft", ...props }) => {
1110
- var _effect = _useSignals4();
1111
- try {
1112
- return /* @__PURE__ */ React4.createElement(CustomControl2, {
1113
- position,
1114
- ...props
1115
- }, /* @__PURE__ */ React4.createElement(ZoomControls, {
1116
- onAction
1117
- }));
1118
- } finally {
1119
- _effect.f();
1120
- }
1121
- },
1122
- Action: ({ onAction, position = "bottomright", ...props }) => {
1123
- var _effect = _useSignals4();
1124
- try {
1125
- return /* @__PURE__ */ React4.createElement(CustomControl2, {
1126
- position,
1127
- ...props
1128
- }, /* @__PURE__ */ React4.createElement(ActionControls, {
1129
- onAction
1130
- }));
1131
- } finally {
1132
- _effect.f();
1133
- }
1134
- }
1075
+ Tiles: MapTiles,
1076
+ Markers: MapMarkers,
1077
+ Zoom: MapZoom,
1078
+ Action: MapAction
1135
1079
  };
1136
1080
  export {
1137
1081
  ActionControls,
@@ -1153,6 +1097,8 @@ export {
1153
1097
  renderLayers,
1154
1098
  restrictAxis,
1155
1099
  timer,
1100
+ translationKey,
1101
+ translations,
1156
1102
  useDrag,
1157
1103
  useGlobeContext,
1158
1104
  useGlobeZoomHandler,