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

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