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

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 +399 -446
  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 +399 -446
  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 +10 -4
  8. package/dist/types/src/components/Globe/Globe.d.ts.map +1 -1
  9. package/dist/types/src/components/Globe/Globe.stories.d.ts +31 -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 +38 -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 +6 -8
  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 +30 -23
  40. package/src/components/Globe/Globe.stories.tsx +83 -35
  41. package/src/components/Globe/Globe.tsx +101 -54
  42. package/src/components/Map/Map.stories.tsx +28 -15
  43. package/src/components/Map/Map.tsx +220 -94
  44. package/src/components/Toolbar/Controls.tsx +14 -20
  45. package/src/components/index.ts +0 -2
  46. package/src/hooks/context.tsx +11 -34
  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,43 +3,16 @@ 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";
8
- import React3, { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useRef, useState as useState3 } from "react";
6
+ import { easeLinear, easeSinOut, geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition } from "d3";
7
+ import React2, { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useRef, useState as useState3 } from "react";
9
8
  import { useResizeDetector } from "react-resize-detector";
10
- import { useDynamicRef, useThemeContext } from "@dxos/react-ui";
11
- import { mx } from "@dxos/react-ui-theme";
9
+ import { useComposedRefs, useControlledState, useDynamicRef, useThemeContext } from "@dxos/react-ui";
10
+ import { composable, composableProps, 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
- import React, { createContext, useContext } from "react";
13
+ import { createContext, useContext } from "react";
16
14
  import { raise } from "@dxos/debug";
17
- import { useControlledState } from "@dxos/react-ui";
18
15
  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
- }
42
- };
43
16
  var useGlobeContext = () => {
44
17
  return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
45
18
  };
@@ -64,7 +37,7 @@ var timer = (cb) => {
64
37
  };
65
38
 
66
39
  // src/util/inertia.ts
67
- import { select, drag, timer as timer2 } from "d3";
40
+ import { drag, select, timer as timer2 } from "d3";
68
41
  import versor from "versor";
69
42
  var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
70
43
  var geoInertiaDrag = (target, render, projection, options) => {
@@ -427,6 +400,7 @@ var cancelDrag = (node) => node.on(".drag", null);
427
400
 
428
401
  // src/hooks/useGlobeZoomHandler.ts
429
402
  import { useCallback } from "react";
403
+ var ZOOM_FACTOR = 0.1;
430
404
  var useGlobeZoomHandler = (controller) => {
431
405
  return useCallback((event) => {
432
406
  if (!controller) {
@@ -434,11 +408,15 @@ var useGlobeZoomHandler = (controller) => {
434
408
  }
435
409
  switch (event) {
436
410
  case "zoom-in": {
437
- controller.setScale((scale) => scale * 1.1);
411
+ controller.setZoom((zoom) => {
412
+ return zoom * (1 + ZOOM_FACTOR);
413
+ });
438
414
  break;
439
415
  }
440
416
  case "zoom-out": {
441
- controller.setScale((scale) => scale * 0.9);
417
+ controller.setZoom((zoom) => {
418
+ return zoom * (1 - ZOOM_FACTOR);
419
+ });
442
420
  break;
443
421
  }
444
422
  }
@@ -523,8 +501,8 @@ var useSpinner = (controller, options = {}) => {
523
501
  };
524
502
 
525
503
  // 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";
504
+ import { selection as d3Selection, geoDistance, geoInterpolate, geoPath } from "d3";
505
+ import { useEffect as useEffect3, useMemo, useState as useState2 } from "react";
528
506
  import versor2 from "versor";
529
507
  var TRANSITION_NAME = "globe-tour";
530
508
  var defaultDuration = 1500;
@@ -571,7 +549,7 @@ var useTour = (controller, points, options = {}) => {
571
549
  {
572
550
  context.beginPath();
573
551
  context.strokeStyle = options?.styles?.arc?.strokeStyle ?? "yellow";
574
- context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.scale ?? 1);
552
+ context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.zoom ?? 1);
575
553
  context.setLineDash(options?.styles?.arc?.lineDash ?? []);
576
554
  path({
577
555
  type: "LineString",
@@ -583,7 +561,7 @@ var useTour = (controller, points, options = {}) => {
583
561
  context.stroke();
584
562
  context.beginPath();
585
563
  context.fillStyle = options?.styles?.cursor?.fillStyle ?? "orange";
586
- path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.scale ?? 1));
564
+ path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.zoom ?? 1));
587
565
  path({
588
566
  type: "Point",
589
567
  coordinates: ip(t22)
@@ -597,7 +575,7 @@ var useTour = (controller, points, options = {}) => {
597
575
  await transition2.end();
598
576
  last = next;
599
577
  }
600
- } catch (err) {
578
+ } catch {
601
579
  } finally {
602
580
  setRunning(false);
603
581
  }
@@ -619,9 +597,25 @@ var useTour = (controller, points, options = {}) => {
619
597
  };
620
598
 
621
599
  // src/components/Toolbar/Controls.tsx
622
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
623
- import React2 from "react";
624
- import { IconButton, Toolbar } from "@dxos/react-ui";
600
+ import React from "react";
601
+ import { IconButton, Toolbar, useTranslation } from "@dxos/react-ui";
602
+
603
+ // src/translations.ts
604
+ var translationKey = "@dxos/react-ui-geo";
605
+ var translations = [
606
+ {
607
+ "en-US": {
608
+ [translationKey]: {
609
+ "zoom-in-icon.button": "Zoom in",
610
+ "zoom-out-icon.button": "Zoom out",
611
+ "start-icon.button": "Start",
612
+ "toggle-icon.button": "Toggle"
613
+ }
614
+ }
615
+ }
616
+ ];
617
+
618
+ // src/components/Toolbar/Controls.tsx
625
619
  var controlPositions = {
626
620
  topleft: "top-2 left-2",
627
621
  topright: "top-2 right-2",
@@ -629,62 +623,42 @@ var controlPositions = {
629
623
  bottomright: "bottom-2 right-2"
630
624
  };
631
625
  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
- }
626
+ const { t } = useTranslation(translationKey);
627
+ return /* @__PURE__ */ React.createElement(Toolbar.Root, {
628
+ classNames: [
629
+ "gap-2",
630
+ classNames
631
+ ]
632
+ }, /* @__PURE__ */ React.createElement(IconButton, {
633
+ icon: "ph--plus--regular",
634
+ iconOnly: true,
635
+ label: t("zoom-in-icon.button"),
636
+ onClick: () => onAction?.("zoom-in")
637
+ }), /* @__PURE__ */ React.createElement(IconButton, {
638
+ icon: "ph--minus--regular",
639
+ iconOnly: true,
640
+ label: t("zoom-out-icon.button"),
641
+ onClick: () => onAction?.("zoom-out")
642
+ }));
659
643
  };
660
644
  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
- }
645
+ const { t } = useTranslation(translationKey);
646
+ return /* @__PURE__ */ React.createElement(Toolbar.Root, {
647
+ classNames: [
648
+ "gap-2",
649
+ classNames
650
+ ]
651
+ }, /* @__PURE__ */ React.createElement(IconButton, {
652
+ icon: "ph--path--regular",
653
+ iconOnly: true,
654
+ label: t("start-icon.button"),
655
+ onClick: () => onAction?.("start")
656
+ }), /* @__PURE__ */ React.createElement(IconButton, {
657
+ icon: "ph--globe-hemisphere-west--regular",
658
+ iconOnly: true,
659
+ label: t("toggle-icon.button"),
660
+ onClick: () => onAction?.("toggle")
661
+ }));
688
662
  };
689
663
 
690
664
  // src/components/Globe/Globe.tsx
@@ -742,400 +716,377 @@ var getProjection = (type = "orthographic") => {
742
716
  }
743
717
  return type ?? geoOrthographic();
744
718
  };
745
- 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, {
719
+ var GlobeRoot = composable(({ children, center: centerProp, zoom: zoomProp, translation: translationProp, rotation: rotationProp, ...props }, forwardedRef) => {
720
+ const localRef = useRef(null);
721
+ const composedRef = useComposedRefs(localRef, forwardedRef);
722
+ const { width, height } = useResizeDetector({
723
+ targetRef: localRef
724
+ });
725
+ const [center, setCenter] = useControlledState(centerProp);
726
+ const [zoom, setZoom] = useControlledState(zoomProp ?? 4);
727
+ const [translation, setTranslation] = useControlledState(translationProp);
728
+ const [rotation, setRotation] = useControlledState(rotationProp);
729
+ return /* @__PURE__ */ React2.createElement(GlobeContext.Provider, {
730
+ value: {
753
731
  size: {
754
732
  width,
755
733
  height
756
734
  },
757
- ...props
758
- }, children));
759
- } finally {
760
- _effect.f();
761
- }
762
- };
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
- })), [
735
+ center,
736
+ zoom,
737
+ translation,
738
+ rotation,
739
+ setCenter,
740
+ setZoom,
741
+ setTranslation,
742
+ setRotation
743
+ }
744
+ }, /* @__PURE__ */ React2.createElement("div", {
745
+ ...composableProps(props, {
746
+ classNames: "relative dx-container"
747
+ }),
748
+ ref: composedRef
749
+ }, children));
750
+ });
751
+ var GlobeCanvas = /* @__PURE__ */ forwardRef(({ projection: projectionProp, topology, features, styles: stylesProp }, forwardRef3) => {
752
+ const { themeMode } = useThemeContext();
753
+ const styles = useMemo2(() => stylesProp ?? defaultStyles[themeMode], [
754
+ stylesProp,
755
+ themeMode
756
+ ]);
757
+ const [canvas, setCanvas] = useState3(null);
758
+ const canvasRef = (canvas2) => setCanvas(canvas2);
759
+ const projection = useMemo2(() => getProjection(projectionProp), [
760
+ projectionProp
761
+ ]);
762
+ const layers = useMemo2(() => {
763
+ return timer(() => createLayers(topology, features, styles));
764
+ }, [
765
+ topology,
766
+ features,
767
+ styles
768
+ ]);
769
+ const { size, center, zoom, translation, rotation, setCenter, setZoom, setTranslation, setRotation } = useGlobeContext();
770
+ const zoomRef = useDynamicRef(zoom);
771
+ useEffect4(() => {
772
+ if (center) {
773
+ setZoom(1);
774
+ setRotation(positionToRotation(geoToPosition(center)));
775
+ }
776
+ }, [
777
+ center
778
+ ]);
779
+ const zooming = useRef(false);
780
+ useImperativeHandle(forwardRef3, () => {
781
+ return {
824
782
  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,
783
+ projection,
784
+ center,
785
+ get zoom() {
786
+ return zoomRef.current;
787
+ },
845
788
  translation,
846
789
  rotation,
847
- layers
848
- ]);
849
- if (!size.width || !size.height) {
850
- return null;
790
+ setCenter,
791
+ setZoom: (state) => {
792
+ if (typeof state === "function") {
793
+ const is = interpolateNumber(zoomRef.current, state(zoomRef.current));
794
+ transition().ease(zooming.current ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => setZoom(is(t))).on("end", () => {
795
+ zooming.current = false;
796
+ });
797
+ } else {
798
+ setZoom(state);
799
+ }
800
+ },
801
+ setTranslation,
802
+ setRotation
803
+ };
804
+ }, [
805
+ canvas
806
+ ]);
807
+ const generator = useMemo2(() => canvas && projection && geoPath2(projection, canvas.getContext("2d", {
808
+ alpha: false
809
+ })), [
810
+ canvas,
811
+ projection
812
+ ]);
813
+ useEffect4(() => {
814
+ if (canvas && projection) {
815
+ timer(() => {
816
+ projection.scale(Math.min(size.width, size.height) / 2 * zoom).translate([
817
+ size.width / 2 + (translation?.x ?? 0),
818
+ size.height / 2 + (translation?.y ?? 0)
819
+ ]).rotate(rotation ?? [
820
+ 0,
821
+ 0,
822
+ 0
823
+ ]);
824
+ renderLayers(generator, layers, zoom, styles);
825
+ });
851
826
  }
852
- return /* @__PURE__ */ React3.createElement("canvas", {
853
- ref: canvasRef,
854
- width: size.width,
855
- height: size.height
856
- });
857
- } finally {
858
- _effect.f();
827
+ }, [
828
+ generator,
829
+ size,
830
+ zoom,
831
+ translation,
832
+ rotation,
833
+ layers
834
+ ]);
835
+ if (!size.width || !size.height) {
836
+ return null;
859
837
  }
838
+ return /* @__PURE__ */ React2.createElement("canvas", {
839
+ ref: canvasRef,
840
+ width: size.width,
841
+ height: size.height
842
+ });
860
843
  });
861
844
  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
- }
845
+ const { size, zoom, translation, rotation } = useGlobeContext();
846
+ return /* @__PURE__ */ React2.createElement("div", {
847
+ className: mx("z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded-sm", controlPositions[position])
848
+ }, /* @__PURE__ */ React2.createElement("pre", {
849
+ className: "font-mono text-xs text-green-700"
850
+ }, JSON.stringify({
851
+ size,
852
+ zoom,
853
+ translation,
854
+ rotation
855
+ }, null, 2)));
878
856
  };
879
857
  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
- }
858
+ return /* @__PURE__ */ React2.createElement("div", {
859
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
860
+ }, children);
888
861
  };
889
862
  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
- }
863
+ return /* @__PURE__ */ React2.createElement("div", {
864
+ className: mx("z-10 absolute overflow-hidden", controlPositions[position])
865
+ }, children);
898
866
  };
867
+ var GlobeZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React2.createElement(CustomControl, {
868
+ position,
869
+ ...props
870
+ }, /* @__PURE__ */ React2.createElement(ZoomControls, {
871
+ onAction
872
+ }));
873
+ var GlobeAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React2.createElement(CustomControl, {
874
+ position,
875
+ ...props
876
+ }, /* @__PURE__ */ React2.createElement(ActionControls, {
877
+ onAction
878
+ }));
899
879
  var Globe = {
900
880
  Root: GlobeRoot,
901
881
  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
- },
882
+ Zoom: GlobeZoom,
883
+ Action: GlobeAction,
928
884
  Debug: GlobeDebug,
929
885
  Panel: GlobePanel
930
886
  };
931
887
 
932
888
  // src/components/Map/Map.tsx
933
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
934
889
  import "leaflet/dist/leaflet.css";
890
+ import { createContext as createContext2 } from "@radix-ui/react-context";
935
891
  import L, { Control, DomEvent, DomUtil, latLngBounds } from "leaflet";
936
- import React4, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2 } from "react";
892
+ import React3, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2, useRef as useRef2 } from "react";
937
893
  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";
894
+ import { MapContainer, Marker, Popup, TileLayer, useMap, useMapEvents } from "react-leaflet";
941
895
  import { ThemeProvider, Tooltip } from "@dxos/react-ui";
942
- import { defaultTx, mx as mx2 } from "@dxos/react-ui-theme";
896
+ import { composable as composable2, composableProps as composableProps2, defaultTx, mx as mx2 } from "@dxos/ui-theme";
943
897
  var defaults = {
944
- // TODO(burdon): Guess location.
945
898
  center: {
946
899
  lat: 51,
947
900
  lng: 0
948
901
  },
949
902
  zoom: 4
950
903
  };
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
- }
904
+ var [MapContextProvider, useMapContext] = createContext2("Map");
905
+ var MapRoot = composable2(({ children, onChange, ...props }, forwardedRef) => {
906
+ const attention = false;
907
+ return /* @__PURE__ */ React3.createElement(MapContextProvider, {
908
+ attention,
909
+ onChange
910
+ }, /* @__PURE__ */ React3.createElement("div", {
911
+ ...composableProps2(props, {
912
+ role: "none",
913
+ classNames: "dx-container grid dx-focus-ring-inset"
914
+ }),
915
+ ref: forwardedRef
916
+ }, children));
917
+ });
918
+ MapRoot.displayName = "Map.Root";
919
+ var MAP_CONTENT_NAME = "Map.Content";
920
+ var MapContent = /* @__PURE__ */ forwardRef2(({ classNames, scrollWheelZoom = true, doubleClickZoom = true, touchZoom = true, center, zoom, children, ...props }, forwardedRef) => {
921
+ const { attention } = useMapContext(MAP_CONTENT_NAME);
922
+ const mapRef = useRef2(null);
923
+ const map = mapRef.current;
924
+ useImperativeHandle2(forwardedRef, () => ({
925
+ setCenter: (center2, zoom2) => {
926
+ mapRef.current?.setView(center2, zoom2);
927
+ },
928
+ setZoom: (cb) => {
929
+ mapRef.current?.setZoom(cb(mapRef.current?.getZoom() ?? 0));
930
+ }
931
+ }), []);
932
+ useEffect5(() => {
933
+ if (!map) {
934
+ return;
935
+ }
936
+ if (attention) {
937
+ map.scrollWheelZoom.enable();
938
+ } else {
939
+ map.scrollWheelZoom.disable();
940
+ }
941
+ }, [
942
+ map,
943
+ attention
944
+ ]);
945
+ return /* @__PURE__ */ React3.createElement(MapContainer, {
946
+ ...props,
947
+ className: mx2("group relative grid bg-base-surface!", classNames),
948
+ attributionControl: false,
949
+ zoomControl: false,
950
+ scrollWheelZoom,
951
+ doubleClickZoom,
952
+ touchZoom,
953
+ center: center ?? defaults.center,
954
+ zoom: zoom ?? defaults.zoom,
955
+ whenReady: () => {
956
+ },
957
+ ref: mapRef
958
+ }, children);
959
+ });
960
+ MapContent.displayName = "Map.Content";
961
+ var MAP_TILES_NAME = "Map.Tiles";
962
+ var MapTiles = (_props) => {
963
+ const ref = useRef2(null);
964
+ const { onChange } = useMapContext(MAP_TILES_NAME);
965
+ useMapEvents({
966
+ zoomstart: (ev) => {
967
+ onChange?.({
968
+ center: ev.target.getCenter(),
969
+ zoom: ev.target.getZoom()
970
+ });
971
+ }
972
+ });
973
+ const { attention } = useMapContext(MAP_TILES_NAME);
974
+ useEffect5(() => {
975
+ if (ref.current) {
976
+ ref.current.getContainer().dataset.attention = attention ? "1" : "0";
977
+ }
978
+ }, [
979
+ attention
980
+ ]);
981
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(TileLayer, {
982
+ ref,
983
+ "data-attention": attention,
984
+ detectRetina: true,
985
+ className: 'dark:grayscale dark:invert data-[attention="0"]:!opacity-80',
986
+ url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
987
+ keepBuffer: 4
988
+ }));
967
989
  };
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);
990
+ MapTiles.displayName = MAP_TILES_NAME;
991
+ var MapMarkers = ({ selected, markers }) => {
992
+ const map = useMap();
993
+ useEffect5(() => {
994
+ if (markers.length > 0) {
995
+ const bounds = latLngBounds(markers.map((marker) => marker.location));
996
+ map.fitBounds(bounds);
997
+ } else {
998
+ map.setView(defaults.center, defaults.zoom);
999
+ }
1000
+ }, [
1001
+ markers
1002
+ ]);
1003
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, markers?.map(({ id, title, location: { lat, lng } }) => {
1004
+ return /* @__PURE__ */ React3.createElement(Marker, {
1005
+ key: id,
1006
+ position: {
1007
+ lat,
1008
+ lng
978
1009
  },
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
- });
1010
+ icon: (
1011
+ // TODO(burdon): Create custom icon from bundled assets.
1012
+ // TODO(burdon): Selection state.
1013
+ new L.Icon({
1014
+ iconUrl: "https://dxos.network/marker-icon.png",
1015
+ iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
1016
+ shadowUrl: "https://dxos.network/marker-shadow.png",
1017
+ iconSize: [
1018
+ 25,
1019
+ 41
1020
+ ],
1021
+ iconAnchor: [
1022
+ 12,
1023
+ 41
1024
+ ],
1025
+ popupAnchor: [
1026
+ 1,
1027
+ -34
1028
+ ],
1029
+ shadowSize: [
1030
+ 41,
1031
+ 41
1032
+ ]
1033
+ })
1034
+ )
1035
+ }, title && /* @__PURE__ */ React3.createElement(Popup, null, title));
1036
+ }));
1037
+ };
1038
+ MapMarkers.displayName = "Map.Markers";
1073
1039
  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
- }
1040
+ const map = useMap();
1041
+ useEffect5(() => {
1042
+ const control = new Control({
1043
+ position
1044
+ });
1045
+ control.onAdd = () => {
1046
+ const container = DomUtil.create("div", mx2("m-0!", controlPositions[position]));
1047
+ DomEvent.disableClickPropagation(container);
1048
+ DomEvent.disableScrollPropagation(container);
1049
+ const root = createRoot(container);
1050
+ root.render(/* @__PURE__ */ React3.createElement(ThemeProvider, {
1051
+ tx: defaultTx
1052
+ }, /* @__PURE__ */ React3.createElement(Tooltip.Provider, null, children)));
1053
+ return container;
1054
+ };
1055
+ control.addTo(map);
1056
+ return () => {
1057
+ control.remove();
1058
+ };
1059
+ }, [
1060
+ map,
1061
+ position,
1062
+ children
1063
+ ]);
1064
+ return null;
1104
1065
  };
1066
+ var MapZoom = ({ onAction, position = "bottomleft", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl2, {
1067
+ position,
1068
+ ...props
1069
+ }, /* @__PURE__ */ React3.createElement(ZoomControls, {
1070
+ onAction
1071
+ }));
1072
+ var MapAction = ({ onAction, position = "bottomright", ...props }) => /* @__PURE__ */ React3.createElement(CustomControl2, {
1073
+ position,
1074
+ ...props
1075
+ }, /* @__PURE__ */ React3.createElement(ActionControls, {
1076
+ onAction
1077
+ }));
1105
1078
  var Map = {
1106
1079
  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
- }
1080
+ Content: MapContent,
1081
+ Tiles: MapTiles,
1082
+ Markers: MapMarkers,
1083
+ Zoom: MapZoom,
1084
+ Action: MapAction
1134
1085
  };
1135
1086
  export {
1136
1087
  ActionControls,
1137
1088
  Globe,
1138
- GlobeContextProvider,
1089
+ GlobeContext,
1139
1090
  Map,
1140
1091
  ZoomControls,
1141
1092
  closestPoint,
@@ -1152,6 +1103,8 @@ export {
1152
1103
  renderLayers,
1153
1104
  restrictAxis,
1154
1105
  timer,
1106
+ translationKey,
1107
+ translations,
1155
1108
  useDrag,
1156
1109
  useGlobeContext,
1157
1110
  useGlobeZoomHandler,