import {
  loadTopology
} from "./chunk-ENCWOTYX.mjs";

// packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
import { geoMercator, geoOrthographic, geoPath as geoPath2, geoTransverseMercator, interpolateNumber, transition, easeLinear, easeSinOut } from "d3";
import React3, { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useRef, useState as useState3 } from "react";
import { useResizeDetector } from "react-resize-detector";
import { useDynamicRef, useThemeContext } from "@dxos/react-ui";
import { mx } from "@dxos/react-ui-theme";

// packages/ui/react-ui-geo/src/hooks/context.tsx
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
import React, { createContext, useContext } from "react";
import { raise } from "@dxos/debug";
import { useControlledState } from "@dxos/react-ui";
var GlobeContext = /* @__PURE__ */ createContext(void 0);
var GlobeContextProvider = ({ children, size, center: _center, scale: _scale, translation: _translation, rotation: _rotation }) => {
  var _effect = _useSignals();
  try {
    const [center, setCenter] = useControlledState(_center);
    const [scale, setScale] = useControlledState(_scale);
    const [translation, setTranslation] = useControlledState(_translation);
    const [rotation, setRotation] = useControlledState(_rotation);
    return /* @__PURE__ */ React.createElement(GlobeContext.Provider, {
      value: {
        size,
        center,
        scale,
        translation,
        rotation,
        setCenter,
        setScale,
        setTranslation,
        setRotation
      }
    }, children);
  } finally {
    _effect.f();
  }
};
var useGlobeContext = () => {
  return useContext(GlobeContext) ?? raise(new Error("Missing GlobeContext"));
};

// packages/ui/react-ui-geo/src/hooks/useDrag.ts
import { select as select2 } from "d3";
import { useEffect } from "react";

// packages/ui/react-ui-geo/src/util/debug.ts
var debug = false;
var timer = (cb) => {
  const start = Date.now();
  const data = cb();
  const t = Date.now() - start / 1e3;
  if (debug) {
    console.log({
      t,
      data
    });
  }
  return data;
};

// packages/ui/react-ui-geo/src/util/inertia.ts
import { select, drag, timer as timer2 } from "d3";
import versor from "versor";
var restrictAxis = (axis) => (original, current) => current.map((d, i) => axis[i] ? d : original[i]);
var geoInertiaDrag = (target, render, projection, options) => {
  if (!options) {
    options = {};
  }
  if (target.node) {
    target = target.node();
  }
  target = select(target);
  const inertia = geoInertiaDragHelper({
    projection,
    render: (rotation) => {
      projection.rotate(rotation);
      render && render();
    },
    axis: restrictAxis(options.xAxis ? [
      true,
      false,
      false
    ] : [
      true,
      true,
      true
    ]),
    start: options.start,
    move: options.move,
    end: options.end,
    stop: options.stop,
    finish: options.finish,
    time: options.time,
    hold: options.hold
  });
  target.call(drag().on("start", inertia.start).on("drag", inertia.move).on("end", inertia.end));
  return inertia;
};
var geoInertiaDragHelper = (opt) => {
  const projection = opt.projection;
  let v0;
  let r0;
  let q0;
  let v10;
  let v11;
  let q10;
  const inertia = inertiaHelper({
    axis: opt.axis,
    start: () => {
      v0 = versor.cartesian(projection.invert(inertia.position));
      r0 = projection.rotate();
      q0 = versor(r0);
      opt.start && opt.start();
    },
    move: () => {
      const inv = projection.rotate(r0).invert(inertia.position);
      if (isNaN(inv[0])) {
        return;
      }
      const v1 = versor.cartesian(inv);
      const q1 = versor.multiply(q0, versor.delta(v0, v1));
      const r1 = versor.rotation(q1);
      const r2 = opt.axis(r0, r1);
      opt.render(r2);
      opt.move && opt.move();
    },
    end: () => {
      v10 = versor.cartesian(projection.invert(inertia.position.map((d, i) => d - inertia.velocity[i] / 1e3)));
      q10 = versor(projection.rotate());
      v11 = versor.cartesian(projection.invert(inertia.position));
      opt.end && opt.end();
    },
    stop: opt.stop,
    finish: opt.finish,
    render: (t) => {
      const r1 = versor.rotation(versor.multiply(q10, versor.delta(v10, v11, t * 1e3)));
      const r2 = opt.axis(r0, r1);
      opt.render && opt.render(r2);
    },
    time: opt.time
  });
  return inertia;
};
function inertiaHelper(opt) {
  const A = opt.time || 5e3;
  const limit = 1.0001;
  const B = -Math.log(1 - 1 / limit);
  const inertia = {
    position: [
      0,
      0
    ],
    velocity: [
      0,
      0
    ],
    timer: timer2(() => {
    }),
    time: 0,
    t: 0,
    start: function(ev) {
      const position = [
        ev.x,
        ev.y
      ];
      inertia.position = position;
      inertia.velocity = [
        0,
        0
      ];
      inertia.timer.stop();
      this.classList.remove("inertia");
      this.classList.add("dragging");
      opt.start && opt.start.call(this, position);
    },
    move: function(ev) {
      const position = [
        ev.x,
        ev.y
      ];
      const time = performance.now();
      const deltaTime = time - inertia.time;
      const decay = 1 - Math.exp(-deltaTime / 1e3);
      inertia.velocity = inertia.velocity.map((d, i) => {
        const deltaPos = position[i] - inertia.position[i];
        const deltaTime2 = time - inertia.time;
        return 1e3 * (1 - decay) * deltaPos / deltaTime2 + d * decay;
      });
      inertia.velocity = opt.axis([
        0,
        0
      ], inertia.velocity);
      inertia.time = time;
      inertia.position = position;
      opt.move && opt.move.call(this, position);
    },
    end: function(ev) {
      this.classList.remove("dragging", "inertia");
      const v = inertia.velocity;
      if (v[0] * v[0] + v[1] * v[1] < 100) {
        inertia.timer.stop();
        return opt.stop && opt.stop();
      }
      const time = performance.now();
      const deltaTime = time - inertia.time;
      if (opt.hold === void 0) {
        opt.hold = 100;
      }
      if (deltaTime >= opt.hold) {
        inertia.timer.stop();
        return opt.stop && opt.stop();
      }
      this.classList.add("inertia");
      opt.end && opt.end();
      const self = this;
      inertia.timer.restart((e) => {
        inertia.t = limit * (1 - Math.exp(-B * e / A));
        opt.render && opt.render(inertia.t);
        if (inertia.t > 1) {
          inertia.timer.stop();
          self.classList.remove("inertia");
          inertia.velocity = [
            0,
            0
          ];
          inertia.t = 1;
          opt.finish && opt.finish();
        }
      });
    }
  };
  inertia.timer.stop();
  return inertia;
}

// packages/ui/react-ui-geo/src/util/path.ts
import { geoCircle as d3GeoCircle } from "d3";
var positionToRotation = ([lng, lat], tilt = 0) => [
  -lng,
  tilt - lat,
  0
];
var geoToPosition = ({ lat, lng }) => [
  lng,
  lat
];
var geoPoint = (point) => ({
  type: "Point",
  coordinates: geoToPosition(point)
});
var geoCircle = ({ lat, lng }, radius) => d3GeoCircle().radius(radius).center([
  lng,
  lat
])();
var geoLine = (p1, p2) => ({
  type: "LineString",
  coordinates: [
    [
      p1.lng,
      p1.lat
    ],
    [
      p2.lng,
      p2.lat
    ]
  ]
});
var closestPoint = (points, target) => {
  if (points.length === 0) {
    return target;
  }
  let closestPoint2 = points[0];
  let minDistance = getDistance(points[0], target);
  for (const point of points) {
    const distance = getDistance(point, target);
    if (distance < minDistance) {
      minDistance = distance;
      closestPoint2 = point;
    }
  }
  return closestPoint2;
};
var getDistance = (point1, point2) => {
  const dx = point1[0] - point2[0];
  const dy = point1[1] - point2[1];
  return Math.sqrt(dx * dx + dy * dy);
};

// packages/ui/react-ui-geo/src/util/render.ts
import { geoGraticule } from "d3";
import { feature, mesh } from "topojson-client";
var createLayers = (topology, features, styles) => {
  const layers = [];
  if (styles.water) {
    layers.push({
      styles: styles.water,
      path: {
        type: "Sphere"
      }
    });
  }
  if (styles.graticule) {
    layers.push({
      styles: styles.graticule,
      path: geoGraticule().step([
        6,
        6
      ])()
    });
  }
  if (topology) {
    if (topology.objects.land && styles.land) {
      layers.push({
        styles: styles.land,
        path: feature(topology, topology.objects.land)
      });
    }
    if (topology.objects.countries && styles.border) {
      layers.push({
        styles: styles.border,
        path: mesh(topology, topology.objects.countries, (a, b) => a !== b)
      });
    }
    if (topology.objects.dots && styles.dots) {
      layers.push({
        styles: styles.dots,
        path: topology.objects.dots
      });
    }
  }
  if (features) {
    const { points, lines } = features;
    if (points && styles.point) {
      layers.push({
        styles: styles.point,
        path: {
          type: "GeometryCollection",
          geometries: points.map((point) => geoPoint(point))
        }
      });
    }
    if (lines && styles.line) {
      layers.push({
        styles: styles.line,
        path: {
          type: "GeometryCollection",
          geometries: lines.map(({ source, target }) => geoLine(source, target))
        }
      });
    }
  }
  return layers;
};
var renderLayers = (generator, layers = [], scale, styles) => {
  const context = generator.context();
  const { canvas: { width, height } } = context;
  context.reset();
  if (styles.background) {
    context.fillStyle = styles.background.fillStyle;
    context.fillRect(0, 0, width, height);
  } else {
    context.clearRect(0, 0, width, height);
  }
  layers.forEach(({ path, styles: styles2 }) => {
    context.save();
    let fill = false;
    let stroke = false;
    if (styles2) {
      Object.entries(styles2).forEach(([key, value]) => {
        if (key === "pointRadius") {
          generator.pointRadius(value * scale);
        } else {
          context[key] = value;
          fill ||= key === "fillStyle";
          stroke ||= key === "strokeStyle";
        }
      });
    }
    context.beginPath();
    generator(path);
    fill && context.fill();
    stroke && context.stroke();
    context.restore();
  });
  return context;
};

// packages/ui/react-ui-geo/src/hooks/useDrag.ts
var useDrag = (controller, options = {}) => {
  useEffect(() => {
    const canvas = controller?.canvas;
    if (!canvas || options.disabled) {
      return;
    }
    geoInertiaDrag(select2(canvas), () => {
      controller.setRotation(controller.projection.rotate());
      options.onUpdate?.({
        type: "move",
        controller
      });
    }, controller.projection, {
      xAxis: options.xAxis,
      time: 3e3,
      start: () => options.onUpdate?.({
        type: "start",
        controller
      }),
      finish: () => options.onUpdate?.({
        type: "end",
        controller
      })
    });
    return () => {
      cancelDrag(select2(canvas));
    };
  }, [
    controller,
    JSON.stringify(options)
  ]);
};
var cancelDrag = (node) => node.on(".drag", null);

// packages/ui/react-ui-geo/src/hooks/useGlobeZoomHandler.ts
import { useCallback } from "react";
var useGlobeZoomHandler = (controller) => {
  return useCallback((event) => {
    if (!controller) {
      return;
    }
    switch (event) {
      case "zoom-in": {
        controller.setScale((scale) => scale * 1.1);
        break;
      }
      case "zoom-out": {
        controller.setScale((scale) => scale * 0.9);
        break;
      }
    }
  }, [
    controller
  ]);
};

// packages/ui/react-ui-geo/src/hooks/useMapZoomHandler.ts
import { useCallback as useCallback2 } from "react";
var useMapZoomHandler = (controller) => {
  return useCallback2((event) => {
    if (!controller) {
      return;
    }
    switch (event) {
      case "zoom-in": {
        controller.setZoom((scale) => scale + 1);
        break;
      }
      case "zoom-out": {
        controller.setZoom((scale) => scale - 1);
        break;
      }
    }
  }, [
    controller
  ]);
};

// packages/ui/react-ui-geo/src/hooks/useSpinner.ts
import { timer as d3Timer } from "d3";
import { useEffect as useEffect2, useState } from "react";
var useSpinner = (controller, options = {}) => {
  const [running, setRunning] = useState(false);
  useEffect2(() => {
    let timer3;
    const start = () => {
      const delta = options.delta ?? [
        1e-3,
        0,
        0
      ];
      let t = 0;
      let lastRotation = controller.projection.rotate();
      timer3 = d3Timer((elapsed) => {
        const dt = elapsed - t;
        t = elapsed;
        const rotation = [
          lastRotation[0] + delta[0] * dt,
          lastRotation[1] + delta[1] * dt,
          lastRotation[2] + delta[2] * dt
        ];
        lastRotation = rotation;
        controller.setRotation(rotation);
      });
    };
    const stop = () => {
      if (timer3) {
        timer3.stop();
        timer3 = void 0;
      }
    };
    if (controller && running) {
      start();
    } else {
      stop();
    }
    return () => stop();
  }, [
    controller,
    running
  ]);
  return [
    () => {
      if (!options.disabled) {
        setRunning(true);
      }
    },
    () => setRunning(false)
  ];
};

// packages/ui/react-ui-geo/src/hooks/useTour.ts
import { geoPath, geoInterpolate, geoDistance, selection as d3Selection } from "d3";
import { useEffect as useEffect3, useState as useState2, useMemo } from "react";
import versor2 from "versor";
var TRANSITION_NAME = "globe-tour";
var defaultDuration = 1500;
var useTour = (controller, points, options = {}) => {
  const selection = useMemo(() => d3Selection(), []);
  const [running, setRunning] = useState2(options.running ?? false);
  useEffect3(() => {
    if (!running) {
      selection.interrupt(TRANSITION_NAME);
      return;
    }
    let t;
    if (controller && running) {
      t = setTimeout(async () => {
        const { canvas, projection, setRotation } = controller;
        const context = canvas.getContext("2d", {
          alpha: false
        });
        const path = geoPath(projection, context).pointRadius(2);
        const tilt = options.tilt ?? 0;
        let last;
        try {
          const p = [
            ...points
          ];
          if (options.loop) {
            p.push(p[0]);
          }
          for (const next of p) {
            if (!running) {
              break;
            }
            const p1 = last ? geoToPosition(last) : void 0;
            const p2 = geoToPosition(next);
            const ip = geoInterpolate(p1 || p2, p2);
            const distance = geoDistance(p1 || p2, p2);
            const r1 = p1 ? positionToRotation(p1, tilt) : controller.projection.rotate();
            const r2 = positionToRotation(p2, tilt);
            const iv = versor2.interpolate(r1, r2);
            const transition2 = selection.transition(TRANSITION_NAME).duration(Math.max(options.duration ?? defaultDuration, distance * 2e3)).tween("render", () => (t2) => {
              const t1 = Math.max(0, Math.min(1, t2 * 2 - 1));
              const t22 = Math.min(1, t2 * 2);
              context.save();
              {
                context.beginPath();
                context.strokeStyle = options?.styles?.arc?.strokeStyle ?? "yellow";
                context.lineWidth = (options?.styles?.arc?.lineWidth ?? 1.5) * (controller?.scale ?? 1);
                context.setLineDash(options?.styles?.arc?.lineDash ?? []);
                path({
                  type: "LineString",
                  coordinates: [
                    ip(t1),
                    ip(t22)
                  ]
                });
                context.stroke();
                context.beginPath();
                context.fillStyle = options?.styles?.cursor?.fillStyle ?? "orange";
                path.pointRadius((options?.styles?.cursor?.pointRadius ?? 2) * (controller?.scale ?? 1));
                path({
                  type: "Point",
                  coordinates: ip(t22)
                });
                context.fill();
              }
              context.restore();
              projection.rotate(iv(t2));
              setRotation(projection.rotate());
            });
            await transition2.end();
            last = next;
          }
        } catch (err) {
        } finally {
          setRunning(false);
        }
      });
      return () => {
        clearTimeout(t);
        selection.interrupt(TRANSITION_NAME);
      };
    }
  }, [
    controller,
    running,
    JSON.stringify(options)
  ]);
  return [
    running,
    setRunning
  ];
};

// packages/ui/react-ui-geo/src/components/Toolbar/Controls.tsx
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
import React2 from "react";
import { IconButton, Toolbar } from "@dxos/react-ui";
var controlPositions = {
  topleft: "top-2 left-2",
  topright: "top-2 right-2",
  bottomleft: "bottom-2 left-2",
  bottomright: "bottom-2 right-2"
};
var ZoomControls = ({ classNames, onAction }) => {
  var _effect = _useSignals2();
  try {
    return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
      classNames: [
        "gap-1",
        classNames
      ]
    }, /* @__PURE__ */ React2.createElement(IconButton, {
      //
      icon: "ph--plus--regular",
      label: "zoom in",
      iconOnly: true,
      size: 5,
      classNames: "px-0 aspect-square",
      onClick: () => onAction?.("zoom-in")
    }), /* @__PURE__ */ React2.createElement(IconButton, {
      //
      icon: "ph--minus--regular",
      label: "zoom out",
      iconOnly: true,
      size: 5,
      classNames: "px-0 aspect-square",
      onClick: () => onAction?.("zoom-out")
    }));
  } finally {
    _effect.f();
  }
};
var ActionControls = ({ classNames, onAction }) => {
  var _effect = _useSignals2();
  try {
    return /* @__PURE__ */ React2.createElement(Toolbar.Root, {
      classNames: [
        "gap-1",
        classNames
      ]
    }, /* @__PURE__ */ React2.createElement(IconButton, {
      //
      icon: "ph--play--regular",
      label: "start",
      iconOnly: true,
      size: 5,
      classNames: "px-0 aspect-square",
      onClick: () => onAction?.("start")
    }), /* @__PURE__ */ React2.createElement(IconButton, {
      //
      icon: "ph--globe-hemisphere-west--regular",
      label: "toggle",
      iconOnly: true,
      size: 5,
      classNames: "px-0 aspect-square",
      onClick: () => onAction?.("toggle")
    }));
  } finally {
    _effect.f();
  }
};

// packages/ui/react-ui-geo/src/components/Globe/Globe.tsx
var defaultStyles = {
  light: {
    background: {
      fillStyle: "#EEE"
    },
    water: {
      fillStyle: "#555"
    },
    land: {
      fillStyle: "#999"
    },
    line: {
      strokeStyle: "darkred"
    },
    point: {
      fillStyle: "#111111",
      strokeStyle: "#111111",
      strokeWidth: 1,
      pointRadius: 0.5
    }
  },
  dark: {
    background: {
      fillStyle: "#111111"
    },
    water: {
      fillStyle: "#123E6A"
    },
    land: {
      fillStyle: "#032153"
    },
    line: {
      strokeStyle: "#111111"
    },
    point: {
      fillStyle: "#111111",
      strokeStyle: "#111111",
      strokeWidth: 1,
      pointRadius: 0.5
    }
  }
};
var projectionMap = {
  orthographic: geoOrthographic,
  mercator: geoMercator,
  "transverse-mercator": geoTransverseMercator
};
var getProjection = (type = "orthographic") => {
  if (typeof type === "string") {
    const constructor = projectionMap[type] ?? geoOrthographic;
    return constructor();
  }
  return type ?? geoOrthographic();
};
var GlobeRoot = ({ classNames, children, ...props }) => {
  var _effect = _useSignals3();
  try {
    const { ref, width, height } = useResizeDetector();
    return /* @__PURE__ */ React3.createElement("div", {
      ref,
      className: mx("relative flex grow overflow-hidden", classNames)
    }, /* @__PURE__ */ React3.createElement(GlobeContextProvider, {
      size: {
        width,
        height
      },
      ...props
    }, children));
  } finally {
    _effect.f();
  }
};
var GlobeCanvas = /* @__PURE__ */ forwardRef(({ projection: _projection, topology, features, styles: _styles }, forwardRef3) => {
  var _effect = _useSignals3();
  try {
    const { themeMode } = useThemeContext();
    const styles = useMemo2(() => _styles ?? defaultStyles[themeMode], [
      _styles,
      themeMode
    ]);
    const [canvas, setCanvas] = useState3(null);
    const canvasRef = (canvas2) => setCanvas(canvas2);
    const projection = useMemo2(() => getProjection(_projection), [
      _projection
    ]);
    const layers = useMemo2(() => {
      return timer(() => createLayers(topology, features, styles));
    }, [
      topology,
      features,
      styles
    ]);
    const { size, center, scale, translation, rotation, setCenter, setScale, setTranslation, setRotation } = useGlobeContext();
    const scaleRef = useDynamicRef(scale);
    useEffect4(() => {
      if (center) {
        setScale(1);
        setRotation(positionToRotation(geoToPosition(center)));
      }
    }, [
      center
    ]);
    const zooming = useRef(false);
    useImperativeHandle(forwardRef3, () => {
      return {
        canvas,
        projection,
        center,
        get scale() {
          return scaleRef.current;
        },
        translation,
        rotation,
        setCenter,
        setScale: (s) => {
          if (typeof s === "function") {
            const is = interpolateNumber(scaleRef.current, s(scaleRef.current));
            transition().ease(zooming.current ? easeLinear : easeSinOut).duration(200).tween("scale", () => (t) => setScale(is(t))).on("end", () => {
              zooming.current = false;
            });
          } else {
            setScale(s);
          }
        },
        setTranslation,
        setRotation
      };
    }, [
      canvas
    ]);
    const generator = useMemo2(() => canvas && projection && geoPath2(projection, canvas.getContext("2d", {
      alpha: false
    })), [
      canvas,
      projection
    ]);
    useEffect4(() => {
      if (canvas && projection) {
        timer(() => {
          projection.scale(Math.min(size.width, size.height) / 2 * scale).translate([
            size.width / 2 + (translation?.x ?? 0),
            size.height / 2 + (translation?.y ?? 0)
          ]).rotate(rotation ?? [
            0,
            0,
            0
          ]);
          renderLayers(generator, layers, scale, styles);
        });
      }
    }, [
      generator,
      size,
      scale,
      translation,
      rotation,
      layers
    ]);
    if (!size.width || !size.height) {
      return null;
    }
    return /* @__PURE__ */ React3.createElement("canvas", {
      ref: canvasRef,
      width: size.width,
      height: size.height
    });
  } finally {
    _effect.f();
  }
});
var GlobeDebug = ({ position = "topleft" }) => {
  var _effect = _useSignals3();
  try {
    const { size, scale, translation, rotation } = useGlobeContext();
    return /* @__PURE__ */ React3.createElement("div", {
      className: mx("z-10 absolute w-96 p-2 overflow-hidden border border-green-700 rounded", controlPositions[position])
    }, /* @__PURE__ */ React3.createElement("pre", {
      className: "font-mono text-xs text-green-700"
    }, JSON.stringify({
      size,
      scale,
      translation,
      rotation
    }, null, 2)));
  } finally {
    _effect.f();
  }
};
var GlobePanel = ({ position, classNames, children }) => {
  var _effect = _useSignals3();
  try {
    return /* @__PURE__ */ React3.createElement("div", {
      className: mx("z-10 absolute overflow-hidden", controlPositions[position], classNames)
    }, children);
  } finally {
    _effect.f();
  }
};
var CustomControl = ({ position, children }) => {
  var _effect = _useSignals3();
  try {
    return /* @__PURE__ */ React3.createElement("div", {
      className: mx("z-10 absolute overflow-hidden", controlPositions[position])
    }, children);
  } finally {
    _effect.f();
  }
};
var Globe = {
  Root: GlobeRoot,
  Canvas: GlobeCanvas,
  Zoom: ({ onAction, position = "bottomleft", ...props }) => {
    var _effect = _useSignals3();
    try {
      return /* @__PURE__ */ React3.createElement(CustomControl, {
        position,
        ...props
      }, /* @__PURE__ */ React3.createElement(ZoomControls, {
        onAction
      }));
    } finally {
      _effect.f();
    }
  },
  Action: ({ onAction, position = "bottomright", ...props }) => {
    var _effect = _useSignals3();
    try {
      return /* @__PURE__ */ React3.createElement(CustomControl, {
        position,
        ...props
      }, /* @__PURE__ */ React3.createElement(ActionControls, {
        onAction
      }));
    } finally {
      _effect.f();
    }
  },
  Debug: GlobeDebug,
  Panel: GlobePanel
};

// packages/ui/react-ui-geo/src/components/Map/Map.tsx
import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
import "leaflet/dist/leaflet.css";
import L, { Control, DomEvent, DomUtil, latLngBounds } from "leaflet";
import React4, { forwardRef as forwardRef2, useEffect as useEffect5, useImperativeHandle as useImperativeHandle2 } from "react";
import { createRoot } from "react-dom/client";
import { MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet";
import { useResizeDetector as useResizeDetector2 } from "react-resize-detector";
import { debounce } from "@dxos/async";
import { ThemeProvider, Tooltip } from "@dxos/react-ui";
import { defaultTx, mx as mx2 } from "@dxos/react-ui-theme";
var defaults = {
  // TODO(burdon): Guess location.
  center: {
    lat: 51,
    lng: 0
  },
  zoom: 4
};
var MapRoot = ({ classNames, center = defaults.center, zoom = defaults.zoom, ...props }) => {
  var _effect = _useSignals4();
  try {
    return /* @__PURE__ */ React4.createElement(MapContainer, {
      className: mx2("relative grid grow bg-baseSurface", classNames),
      attributionControl: false,
      // TODO(burdon): Only if attention.
      scrollWheelZoom: true,
      zoomControl: false,
      center,
      zoom,
      ...props
    });
  } finally {
    _effect.f();
  }
};
var MapCanvas = /* @__PURE__ */ forwardRef2(({ markers, center, zoom, onChange }, forwardedRef) => {
  var _effect = _useSignals4();
  try {
    const { ref, width, height } = useResizeDetector2({
      refreshRate: 200
    });
    const map = useMap();
    useImperativeHandle2(forwardedRef, () => ({
      setCenter: (center2, zoom2) => {
        map.setView(center2, zoom2);
      },
      setZoom: (cb) => {
        map.setZoom(cb(map.getZoom()));
      }
    }), [
      map
    ]);
    useEffect5(() => {
      if (width && height) {
        map.invalidateSize();
      }
    }, [
      width,
      height
    ]);
    useEffect5(() => {
      if (center) {
        map.setView(center, zoom);
      } else if (zoom !== void 0) {
        map.setZoom(zoom);
      }
    }, [
      center,
      zoom
    ]);
    useEffect5(() => {
      const handler = debounce(() => {
        onChange?.({
          center: map.getCenter(),
          zoom: map.getZoom()
        });
      }, 100);
      map.on("move", handler);
      map.on("zoom", handler);
      return () => {
        map.off("move", handler);
        map.off("zoom", handler);
      };
    }, [
      map,
      onChange
    ]);
    useEffect5(() => {
      if (markers.length > 0) {
        const bounds = latLngBounds(markers.map((marker) => marker.location));
        map.fitBounds(bounds);
      } else {
        map.setView(defaults.center, defaults.zoom);
      }
    }, [
      markers
    ]);
    return /* @__PURE__ */ React4.createElement("div", {
      ref,
      className: "flex w-full h-full overflow-hidden bg-baseSurface"
    }, /* @__PURE__ */ React4.createElement(TileLayer, {
      className: "dark:filter dark:grayscale dark:invert",
      url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    }), markers?.map(({ id, title, location: { lat, lng } }) => {
      return /* @__PURE__ */ React4.createElement(Marker, {
        key: id,
        position: {
          lat,
          lng
        },
        icon: (
          // TODO(burdon): Create custom icon from bundled assets.
          new L.Icon({
            iconUrl: "https://dxos.network/marker-icon.png",
            iconRetinaUrl: "https://dxos.network/marker-icon-2x.png",
            shadowUrl: "https://dxos.network/marker-shadow.png",
            iconSize: [
              25,
              41
            ],
            iconAnchor: [
              12,
              41
            ],
            popupAnchor: [
              1,
              -34
            ],
            shadowSize: [
              41,
              41
            ]
          })
        )
      }, title && /* @__PURE__ */ React4.createElement(Popup, null, title));
    }));
  } finally {
    _effect.f();
  }
});
var CustomControl2 = ({ position, children }) => {
  var _effect = _useSignals4();
  try {
    const map = useMap();
    useEffect5(() => {
      const control = new Control({
        position
      });
      control.onAdd = () => {
        const container = DomUtil.create("div", mx2("!m-0", controlPositions[position]));
        DomEvent.disableClickPropagation(container);
        DomEvent.disableScrollPropagation(container);
        const root = createRoot(container);
        root.render(/* @__PURE__ */ React4.createElement(ThemeProvider, {
          tx: defaultTx
        }, /* @__PURE__ */ React4.createElement(Tooltip.Provider, null, children)));
        return container;
      };
      control.addTo(map);
      return () => {
        control.remove();
      };
    }, [
      map,
      position,
      children
    ]);
    return null;
  } finally {
    _effect.f();
  }
};
var Map = {
  Root: MapRoot,
  Canvas: MapCanvas,
  Zoom: ({ onAction, position = "bottomleft", ...props }) => {
    var _effect = _useSignals4();
    try {
      return /* @__PURE__ */ React4.createElement(CustomControl2, {
        position,
        ...props
      }, /* @__PURE__ */ React4.createElement(ZoomControls, {
        onAction
      }));
    } finally {
      _effect.f();
    }
  },
  Action: ({ onAction, position = "bottomright", ...props }) => {
    var _effect = _useSignals4();
    try {
      return /* @__PURE__ */ React4.createElement(CustomControl2, {
        position,
        ...props
      }, /* @__PURE__ */ React4.createElement(ActionControls, {
        onAction
      }));
    } finally {
      _effect.f();
    }
  }
};
export {
  ActionControls,
  Globe,
  GlobeContextProvider,
  Map,
  ZoomControls,
  closestPoint,
  controlPositions,
  createLayers,
  geoCircle,
  geoInertiaDrag,
  geoLine,
  geoPoint,
  geoToPosition,
  getDistance,
  loadTopology,
  positionToRotation,
  renderLayers,
  restrictAxis,
  timer,
  useDrag,
  useGlobeContext,
  useGlobeZoomHandler,
  useMapZoomHandler,
  useSpinner,
  useTour
};
//# sourceMappingURL=index.mjs.map
