@dxos/react-ui-geo 0.8.4-main.b97322e → 0.8.4-main.bc674ce

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 (59) hide show
  1. package/dist/lib/browser/index.mjs +387 -442
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +387 -442
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Globe/Globe.d.ts +1 -1
  8. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  9. package/dist/types/src/components/Globe/Globe.stories.d.ts +25 -9
  10. package/dist/types/src/components/Globe/Globe.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Map/Map.d.ts +28 -18
  12. package/dist/types/src/components/Map/Map.d.ts.map +1 -1
  13. package/dist/types/src/components/Map/Map.stories.d.ts +14 -8
  14. package/dist/types/src/components/Map/Map.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Toolbar/Controls.d.ts.map +1 -1
  16. package/dist/types/src/components/index.d.ts +0 -1
  17. package/dist/types/src/components/index.d.ts.map +1 -1
  18. package/dist/types/src/hooks/context.d.ts +7 -7
  19. package/dist/types/src/hooks/context.d.ts.map +1 -1
  20. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts +1 -1
  21. package/dist/types/src/hooks/useGlobeZoomHandler.d.ts.map +1 -1
  22. package/dist/types/src/hooks/useMapZoomHandler.d.ts +1 -1
  23. package/dist/types/src/hooks/useMapZoomHandler.d.ts.map +1 -1
  24. package/dist/types/src/hooks/useSpinner.d.ts +1 -1
  25. package/dist/types/src/hooks/useSpinner.d.ts.map +1 -1
  26. package/dist/types/src/hooks/useTour.d.ts +4 -3
  27. package/dist/types/src/hooks/useTour.d.ts.map +1 -1
  28. package/dist/types/src/index.d.ts +1 -0
  29. package/dist/types/src/index.d.ts.map +1 -1
  30. package/dist/types/src/translations.d.ts +12 -0
  31. package/dist/types/src/translations.d.ts.map +1 -0
  32. package/dist/types/src/types.d.ts +2 -1
  33. package/dist/types/src/types.d.ts.map +1 -1
  34. package/dist/types/src/util/path.d.ts +5 -8
  35. package/dist/types/src/util/path.d.ts.map +1 -1
  36. package/dist/types/src/util/render.d.ts +4 -4
  37. package/dist/types/src/util/render.d.ts.map +1 -1
  38. package/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +29 -23
  40. package/src/components/Globe/Globe.stories.tsx +82 -34
  41. package/src/components/Globe/Globe.tsx +56 -35
  42. package/src/components/Map/Map.stories.tsx +25 -14
  43. package/src/components/Map/Map.tsx +181 -96
  44. package/src/components/Toolbar/Controls.tsx +14 -20
  45. package/src/components/index.ts +0 -2
  46. package/src/hooks/context.tsx +22 -16
  47. package/src/hooks/useGlobeZoomHandler.ts +9 -3
  48. package/src/hooks/useMapZoomHandler.ts +1 -1
  49. package/src/hooks/useSpinner.ts +2 -1
  50. package/src/hooks/useTour.ts +10 -8
  51. package/src/index.ts +1 -0
  52. package/src/translations.ts +20 -0
  53. package/src/types.ts +3 -1
  54. package/src/util/inertia.ts +1 -1
  55. package/src/util/path.ts +5 -6
  56. package/src/util/render.ts +5 -3
  57. package/dist/types/src/components/types.d.ts +0 -15
  58. package/dist/types/src/components/types.d.ts.map +0 -1
  59. package/src/components/types.ts +0 -19
@@ -4,42 +4,42 @@ import {
4
4
  } from "./chunk-JODBF4CC.mjs";
5
5
 
6
6
  // 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";
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
13
  // src/hooks/context.tsx
15
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
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"));
@@ -65,7 +65,7 @@ var timer = (cb) => {
65
65
  };
66
66
 
67
67
  // src/util/inertia.ts
68
- import { select, drag, timer as timer2 } from "d3";
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) => {
@@ -428,6 +428,7 @@ var cancelDrag = (node) => node.on(".drag", null);
428
428
 
429
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
  }
@@ -524,8 +529,8 @@ var useSpinner = (controller, options = {}) => {
524
529
  };
525
530
 
526
531
  // 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";
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
  }
@@ -620,9 +625,25 @@ var useTour = (controller, points, options = {}) => {
620
625
  };
621
626
 
622
627
  // src/components/Toolbar/Controls.tsx
623
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
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,62 +651,42 @@ 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
692
  // src/components/Globe/Globe.tsx
@@ -744,394 +745,336 @@ 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: (s) => {
801
+ if (typeof s === "function") {
802
+ const is = interpolateNumber(zoomRef.current, s(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(s);
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
897
  // src/components/Map/Map.tsx
934
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
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 MapTiles = (_props) => {
957
+ const ref = useRef2(null);
958
+ const { onChange } = useMapContext(MapTiles.displayName);
959
+ useMapEvents({
960
+ zoomstart: (ev) => {
961
+ onChange?.({
962
+ center: ev.target.getCenter(),
963
+ zoom: ev.target.getZoom()
964
+ });
965
+ }
966
+ });
967
+ const { attention } = useMapContext(MapTiles.displayName);
968
+ useEffect5(() => {
969
+ if (ref.current) {
970
+ ref.current.getContainer().dataset.attention = attention ? "1" : "0";
971
+ }
972
+ }, [
973
+ attention
974
+ ]);
975
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(TileLayer, {
976
+ ref,
977
+ "data-attention": attention,
978
+ detectRetina: true,
979
+ className: 'dark:grayscale dark:invert data-[attention="0"]:!opacity-80',
980
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
981
+ keepBuffer: 4
982
+ }));
968
983
  };
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);
984
+ MapTiles.displayName = "Map.Tiles";
985
+ var MapMarkers = ({ selected, markers }) => {
986
+ const map = useMap();
987
+ useEffect5(() => {
988
+ if (markers.length > 0) {
989
+ const bounds = latLngBounds(markers.map((marker) => marker.location));
990
+ map.fitBounds(bounds);
991
+ } else {
992
+ map.setView(defaults2.center, defaults2.zoom);
993
+ }
994
+ }, [
995
+ markers
996
+ ]);
997
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, markers?.map(({ id, title, location: { lat, lng } }) => {
998
+ return /* @__PURE__ */ React4.createElement(Marker, {
999
+ key: id,
1000
+ position: {
1001
+ lat,
1002
+ lng
979
1003
  },
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
- });
1004
+ icon: (
1005
+ // TODO(burdon): Create custom icon from bundled assets.
1006
+ // TODO(burdon): Selection state.
1007
+ new L.Icon({
1008
+ iconUrl: "https://dxos.network/marker-icon.png",
1009
+ iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1010
+ shadowUrl: "https://dxos.network/marker-shadow.png",
1011
+ iconSize: [
1012
+ 25,
1013
+ 41
1014
+ ],
1015
+ iconAnchor: [
1016
+ 12,
1017
+ 41
1018
+ ],
1019
+ popupAnchor: [
1020
+ 1,
1021
+ -34
1022
+ ],
1023
+ shadowSize: [
1024
+ 41,
1025
+ 41
1026
+ ]
1027
+ })
1028
+ )
1029
+ }, title && /* @__PURE__ */ React4.createElement(Popup, null, title));
1030
+ }));
1031
+ };
1032
+ MapMarkers.displayName = "Map.Markers";
1074
1033
  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
- }
1034
+ const map = useMap();
1035
+ useEffect5(() => {
1036
+ const control = new Control({
1037
+ position
1038
+ });
1039
+ control.onAdd = () => {
1040
+ const container = DomUtil.create("div", mx2("!m-0", controlPositions[position]));
1041
+ DomEvent.disableClickPropagation(container);
1042
+ DomEvent.disableScrollPropagation(container);
1043
+ const root = createRoot(container);
1044
+ root.render(/* @__PURE__ */ React4.createElement(ThemeProvider, {
1045
+ tx: defaultTx
1046
+ }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, children)));
1047
+ return container;
1048
+ };
1049
+ control.addTo(map);
1050
+ return () => {
1051
+ control.remove();
1052
+ };
1053
+ }, [
1054
+ map,
1055
+ position,
1056
+ children
1057
+ ]);
1058
+ return null;
1105
1059
  };
1060
+ var MapZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
1061
+ position,
1062
+ ...props
1063
+ }, /* @__PURE__ */ React4.createElement(ZoomControls, {
1064
+ onAction
1065
+ }));
1066
+ var MapAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React4.createElement(CustomControl2, {
1067
+ position,
1068
+ ...props
1069
+ }, /* @__PURE__ */ React4.createElement(ActionControls, {
1070
+ onAction
1071
+ }));
1106
1072
  var Map = {
1107
1073
  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
- }
1074
+ Tiles: MapTiles,
1075
+ Markers: MapMarkers,
1076
+ Zoom: MapZoom,
1077
+ Action: MapAction
1135
1078
  };
1136
1079
  export {
1137
1080
  ActionControls,
@@ -1153,6 +1096,8 @@ export {
1153
1096
  renderLayers,
1154
1097
  restrictAxis,
1155
1098
  timer,
1099
+ translationKey,
1100
+ translations,
1156
1101
  useDrag,
1157
1102
  useGlobeContext,
1158
1103
  useGlobeZoomHandler,