@dxos/react-ui-geo 0.8.4-main.84f28bd → 0.8.4-main.8baae0fced

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