@dxos/react-ui-geo 0.8.3 → 0.8.4-main.1068cf700f

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