"use strict";

import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { Pressable, StyleSheet, useWindowDimensions, View } from 'react-native';
import Animated, { Easing, ZoomIn } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useTheme } from "../theme/use-theme.js";
import { atoms as a } from "../styles/index.js";
import { Portal } from '../portal';
import { ARROW_HALF_SIZE, ARROW_SIZE, BUBBLE_MAX_WIDTH, MIN_EDGE_SPACE } from "./const.js";
import { createTextBubble } from "./TextBubble.js";

/**
 * These are native specific values, not shared with web
 */
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const ARROW_VISUAL_OFFSET = ARROW_SIZE / 1.25;
const BUBBLE_SHADOW_OFFSET = ARROW_SIZE / 3;
const TooltipContext = /*#__PURE__*/createContext({
  position: 'bottom',
  visible: false,
  onVisibleChange: () => {}
});
TooltipContext.displayName = 'TooltipContext';
const TargetContext = /*#__PURE__*/createContext({
  targetMeasurements: undefined,
  setTargetMeasurements: () => {},
  shouldMeasure: false
});
TargetContext.displayName = 'TargetContext';

/**
 * Provider for Tooltip component. Only needed when you need to position the
 * tooltip relative to a container, such as in a sheet.
 *
 * Only really necessary on iOS but can work on Android.
 */
export function SheetCompatProvider({
  children
}) {
  return /*#__PURE__*/_jsx(_Fragment, {
    children: children
  });
}
SheetCompatProvider.displayName = 'TooltipSheetCompatProvider';
export function Outer({
  children,
  position = 'bottom',
  visible: requestVisible,
  onVisibleChange
}) {
  /**
   * Lagging state to track the externally-controlled visibility of the
   * tooltip, which needs to wait for the target to be measured before
   * actually being shown.
   */
  const [visible, setVisible] = useState(false);
  const [targetMeasurements, setTargetMeasurements] = useState(undefined);
  if (requestVisible && !visible && targetMeasurements) {
    setVisible(true);
  } else if (!requestVisible && visible) {
    setVisible(false);
    setTargetMeasurements(undefined);
  }
  const ctx = useMemo(() => ({
    position,
    visible,
    onVisibleChange
  }), [position, visible, onVisibleChange]);
  const targetCtx = useMemo(() => ({
    targetMeasurements,
    setTargetMeasurements,
    shouldMeasure: requestVisible
  }), [requestVisible, targetMeasurements, setTargetMeasurements]);
  return /*#__PURE__*/_jsx(TooltipContext.Provider, {
    value: ctx,
    children: /*#__PURE__*/_jsx(TargetContext.Provider, {
      value: targetCtx,
      children: children
    })
  });
}
export function Target({
  children
}) {
  const {
    shouldMeasure,
    setTargetMeasurements
  } = useContext(TargetContext);
  const [hasLayedOut, setHasLayedOut] = useState(false);
  const targetRef = useRef(null);
  useEffect(() => {
    if (!shouldMeasure || !hasLayedOut) return;
    targetRef.current?.measure((_x, _y, width, height, x, y) => {
      if (x !== undefined && y !== undefined && width && height) {
        setTargetMeasurements({
          x,
          y,
          width,
          height
        });
      }
    });
  }, [shouldMeasure, setTargetMeasurements, hasLayedOut]);
  return /*#__PURE__*/_jsx(View, {
    collapsable: false,
    ref: targetRef,
    onLayout: () => setHasLayedOut(true),
    children: children
  });
}
export function Content({
  children,
  label
}) {
  const {
    position,
    visible,
    onVisibleChange
  } = useContext(TooltipContext);
  const {
    targetMeasurements
  } = useContext(TargetContext);
  const requestClose = useCallback(() => {
    onVisibleChange(false);
  }, [onVisibleChange]);
  if (!visible || !targetMeasurements) return null;
  return /*#__PURE__*/_jsx(Portal, {
    children: /*#__PURE__*/_jsx(Bubble, {
      label: label,
      position: position,
      targetMeasurements: targetMeasurements,
      requestClose: requestClose,
      children: children
    })
  });
}
function Bubble({
  children,
  label,
  position,
  requestClose,
  targetMeasurements
}) {
  const theme = useTheme();
  const insets = useSafeAreaInsets();
  const dimensions = useWindowDimensions();
  const [bubbleMeasurements, setBubbleMeasurements] = useState(undefined);
  const bubbleBg = theme.isDark ? {
    backgroundColor: theme.colors.backgroundSecondary
  } : {
    backgroundColor: theme.colors.background
  };
  const coords = useMemo(() => {
    if (!bubbleMeasurements) return {
      computedPosition: position,
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
      tipTop: 0,
      tipLeft: 0
    };
    const {
      width: ww,
      height: wh
    } = dimensions;
    const maxTop = insets.top;
    const maxBottom = wh - insets.bottom;
    const {
      width: cw,
      height: ch
    } = bubbleMeasurements;
    const minLeft = MIN_EDGE_SPACE;
    const maxLeft = ww - minLeft;
    let computedPosition = position;
    let top = targetMeasurements.y + targetMeasurements.height;
    let left = Math.max(minLeft, targetMeasurements.x + targetMeasurements.width / 2 - cw / 2);
    const tipTranslate = ARROW_HALF_SIZE * -1;
    let tipTop = tipTranslate;
    if (left + cw > maxLeft) {
      left -= left + cw - maxLeft;
    }
    const tipLeft = targetMeasurements.x - left + targetMeasurements.width / 2 - ARROW_HALF_SIZE;
    let bottom = top + ch;
    function positionTop() {
      top = top - ch - targetMeasurements.height;
      bottom = top + ch;
      tipTop = tipTop + ch;
      computedPosition = 'top';
    }
    function positionBottom() {
      top = targetMeasurements.y + targetMeasurements.height;
      bottom = top + ch;
      tipTop = tipTranslate;
      computedPosition = 'bottom';
    }
    if (position === 'top') {
      positionTop();
      if (top < maxTop) {
        positionBottom();
      }
    } else {
      if (bottom > maxBottom) {
        positionTop();
      }
    }
    if (computedPosition === 'bottom') {
      top += ARROW_VISUAL_OFFSET;
      bottom += ARROW_VISUAL_OFFSET;
    } else {
      top -= ARROW_VISUAL_OFFSET;
      bottom -= ARROW_VISUAL_OFFSET;
    }
    return {
      computedPosition,
      top,
      bottom,
      left,
      right: left + cw,
      tipTop,
      tipLeft
    };
  }, [position, targetMeasurements, bubbleMeasurements, insets, dimensions]);
  const requestCloseWrapped = useCallback(() => {
    setBubbleMeasurements(undefined);
    requestClose();
  }, [requestClose]);
  return /*#__PURE__*/_jsxs(_Fragment, {
    children: [/*#__PURE__*/_jsx(Pressable, {
      style: StyleSheet.absoluteFill,
      onPress: requestCloseWrapped,
      accessibilityRole: "none"
    }), /*#__PURE__*/_jsx(View, {
      accessible: true,
      role: "alert",
      accessibilityHint: "",
      accessibilityLabel: label,
      importantForAccessibility: "yes",
      accessibilityViewIsModal: true,
      style: [a.absolute, a.align_start, {
        width: BUBBLE_MAX_WIDTH,
        opacity: bubbleMeasurements ? 1 : 0,
        top: coords.top,
        left: coords.left
      }],
      children: /*#__PURE__*/_jsxs(Animated.View, {
        entering: ZoomIn.easing(Easing.out(Easing.exp)),
        style: {
          transformOrigin: opposite(position)
        },
        children: [/*#__PURE__*/_jsx(View, {
          style: [a.absolute, a.top_0, a.z_10, bubbleBg, {
            borderTopLeftRadius: a.rounded_2xs.borderRadius,
            borderBottomRightRadius: a.rounded_2xs.borderRadius,
            width: ARROW_SIZE,
            height: ARROW_SIZE,
            transform: [{
              rotate: '45deg'
            }],
            top: coords.tipTop,
            left: coords.tipLeft
          }]
        }), /*#__PURE__*/_jsx(View, {
          style: [a.px_md, a.py_sm, a.rounded_sm, bubbleBg, {
            shadowColor: theme.colors.shadow,
            shadowOpacity: 0.2,
            shadowRadius: 12,
            shadowOffset: {
              width: 0,
              height: BUBBLE_SHADOW_OFFSET * (coords.computedPosition === 'bottom' ? -1 : 1)
            },
            elevation: 8
          }],
          onLayout: e => {
            setBubbleMeasurements({
              width: e.nativeEvent.layout.width,
              height: e.nativeEvent.layout.height
            });
          },
          children: children
        })]
      })
    })]
  });
}
function opposite(position) {
  switch (position) {
    case 'top':
      return 'center bottom';
    case 'bottom':
      return 'center top';
    default:
      return 'center';
  }
}
export const TextBubble = createTextBubble(Content);
//# sourceMappingURL=index.js.map