@dxos/react-ui-geo 0.8.4-main.3f58842 → 0.8.4-main.40e3dcdf1b

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