"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Content = Content;
exports.Root = Root;
exports.Trigger = Trigger;
Object.defineProperty(exports, "usePopoverContext", {
  enumerable: true,
  get: function () {
    return _context.usePopoverContext;
  }
});
exports.usePopoverControl = usePopoverControl;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _useTheme = require("../theme/use-theme.js");
var _indexWeb = require("../portal/index.web.js");
var _context = require("./context.js");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
/**
 * Web self-contained control. Unlike native (which proxies a `BottomSheet`
 * via `useDialogControl`), the web popover owns its own open state and
 * exposes a `DialogControlProps`-shaped handle for API parity.
 */
function usePopoverControl() {
  const id = (0, _react.useId)();
  const [, setIsOpen] = (0, _react.useState)(false);
  const ref = (0, _react.useRef)({
    open: () => {},
    close: () => {}
  });
  return (0, _react.useMemo)(() => ({
    id,
    ref,
    open: () => setIsOpen(true),
    close: cb => {
      setIsOpen(false);
      cb?.();
    }
  }), [id]);
}
function Root({
  children,
  control
}) {
  const id = (0, _react.useId)();
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
  const triggerRef = (0, _react.useRef)(null);
  const refHandle = (0, _react.useRef)({
    open: () => {},
    close: () => {}
  });
  const activeControl = (0, _react.useMemo)(() => control ?? {
    id,
    ref: refHandle,
    open: () => setIsOpen(true),
    close: cb => {
      setIsOpen(false);
      cb?.();
    }
  }, [control, id]);

  // When an external control is provided, mirror its open/close into local
  // state so Content renders. The external control still drives `open/close`.
  const wrappedControl = (0, _react.useMemo)(() => ({
    ...activeControl,
    open: () => {
      setIsOpen(true);
      activeControl.open();
    },
    close: cb => {
      setIsOpen(false);
      activeControl.close(cb);
    }
  }), [activeControl]);
  const context = (0, _react.useMemo)(() => ({
    control: wrappedControl,
    isOpen,
    triggerRef
  }), [wrappedControl, isOpen]);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_context.PopoverContext.Provider, {
    value: context,
    children: children
  });
}
function Trigger({
  children,
  label
}) {
  const {
    control,
    triggerRef,
    isOpen
  } = (0, _context.usePopoverContext)();
  const [hovered, setHovered] = (0, _react.useState)(false);
  const [focused, setFocused] = (0, _react.useState)(false);
  const rendered = children({
    control,
    state: {
      hovered,
      focused,
      pressed: false
    },
    props: {
      onPress: () => control.open(),
      onFocus: () => setFocused(true),
      onBlur: () => setFocused(false),
      accessibilityLabel: label,
      accessibilityRole: 'button',
      'aria-expanded': isOpen,
      'aria-haspopup': 'dialog'
    }
  });
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
    ref: triggerRef,
    collapsable: false,
    style: styles.triggerWrap,
    onMouseEnter: () => setHovered(true),
    onMouseLeave: () => setHovered(false),
    children: rendered
  });
}
function resolvePosition(rect, panel, placement, offset) {
  const viewportW = typeof window !== 'undefined' ? window.innerWidth : 0;
  const viewportH = typeof window !== 'undefined' ? window.innerHeight : 0;
  const isTop = placement.startsWith('top');
  let top = isTop ? rect.y - panel.height - offset : rect.y + rect.height + offset;

  // Flip if it would overflow the chosen edge.
  if (!isTop && top + panel.height > viewportH && rect.y - panel.height - offset > 0) {
    top = rect.y - panel.height - offset;
  } else if (isTop && top < 0 && rect.y + rect.height + offset + panel.height <= viewportH) {
    top = rect.y + rect.height + offset;
  }
  let left;
  if (placement.endsWith('end')) {
    left = rect.x + rect.width - panel.width;
  } else if (placement.endsWith('start')) {
    left = rect.x;
  } else {
    // bare 'bottom' / 'top' → center-align under the trigger.
    left = rect.x + rect.width / 2 - panel.width / 2;
  }

  // Clamp horizontally into the viewport with an 8px gutter.
  const gutter = 8;
  left = Math.max(gutter, Math.min(left, viewportW - panel.width - gutter));
  top = Math.max(gutter, top);
  return {
    left,
    top
  };
}
function Content({
  children,
  label = 'Popover',
  placement = 'bottom-start',
  offset = 6,
  minWidth,
  maxWidth = 360,
  dismissible = true,
  style,
  testID
}) {
  const theme = (0, _useTheme.useTheme)();
  const {
    control,
    isOpen,
    triggerRef
  } = (0, _context.usePopoverContext)();
  const [rect, setRect] = (0, _react.useState)(null);
  const [panelSize, setPanelSize] = (0, _react.useState)(null);
  const panelRef = (0, _react.useRef)(null);
  const measure = (0, _react.useCallback)(() => {
    const node = triggerRef.current;
    if (!node) return;
    node.measureInWindow((x, y, width, height) => {
      setRect({
        x,
        y,
        width,
        height
      });
    });
  }, [triggerRef]);
  (0, _react.useLayoutEffect)(() => {
    if (!isOpen) {
      setRect(null);
      setPanelSize(null);
      return;
    }
    measure();
  }, [isOpen, measure]);

  // Reposition on scroll / resize while open.
  (0, _react.useEffect)(() => {
    if (!isOpen || typeof window === 'undefined') return;
    const onChange = () => measure();
    window.addEventListener('resize', onChange);
    window.addEventListener('scroll', onChange, true);
    return () => {
      window.removeEventListener('resize', onChange);
      window.removeEventListener('scroll', onChange, true);
    };
  }, [isOpen, measure]);

  // Escape to dismiss.
  (0, _react.useEffect)(() => {
    if (!isOpen || !dismissible || typeof document === 'undefined') return;
    const onKey = e => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        control.close();
      }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [isOpen, dismissible, control]);
  if (!isOpen || !rect) return null;
  const resolvedMinWidth = minWidth ?? rect.width;
  const position = panelSize ? resolvePosition(rect, panelSize, placement, offset) : {
    left: rect.x,
    top: rect.y + rect.height + offset
  };
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_indexWeb.Portal, {
    children: [dismissible ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
      style: styles.backdrop,
      onPress: () => control.close(),
      accessibilityLabel: "Dismiss popover"
    }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
      ref: panelRef,
      accessibilityRole: "none",
      "aria-label": label,
      testID: testID,
      onLayout: e => setPanelSize({
        width: e.nativeEvent.layout.width,
        height: e.nativeEvent.layout.height
      }),
      style: [styles.panel, {
        left: position.left,
        top: position.top,
        minWidth: resolvedMinWidth,
        maxWidth,
        backgroundColor: theme.isDark ? theme.colors.backgroundSecondary : theme.colors.background,
        borderColor: theme.colors.borderLight,
        shadowColor: theme.colors.shadow,
        // Hide until measured to avoid a one-frame jump.
        opacity: panelSize ? 1 : 0
      }, style],
      children: children
    })]
  });
}
const styles = _reactNative.StyleSheet.create({
  triggerWrap: {
    alignSelf: 'flex-start'
  },
  backdrop: {
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: 50,
    pointerEvents: 'auto'
  },
  panel: {
    position: 'fixed',
    zIndex: 60,
    borderRadius: 12,
    borderWidth: 1,
    paddingVertical: 4,
    overflow: 'hidden',
    shadowOpacity: 0.15,
    shadowRadius: 12,
    shadowOffset: {
      width: 0,
      height: 4
    },
    pointerEvents: 'auto'
  }
});
//# sourceMappingURL=index.web.js.map