"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CENTERED_DIALOG_BACKDROP_TESTID = exports.BLOOM_CENTERED_DIALOG_CSS = void 0;
exports.CenteredDialog = CenteredDialog;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactRemoveScrollBar = require("react-remove-scroll-bar");
var _useInteractionState = require("../hooks/useInteractionState.js");
var _Times = require("../icons/Times.js");
var _indexWeb = require("../portal/index.web.js");
var _useTheme = require("../theme/use-theme.js");
var _index = require("../typography/index.js");
var _centeredDialogTokens = require("./centered-dialog-tokens.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); }
const FADE_OUT_DURATION = 150;
const CLOSE_HIT_SLOP = {
  top: 8,
  bottom: 8,
  left: 8,
  right: 8
};

/** Stable testID for the dimmed backdrop. Overridden when a `testID` is set. */
const CENTERED_DIALOG_BACKDROP_TESTID = exports.CENTERED_DIALOG_BACKDROP_TESTID = 'bloom-centered-dialog-backdrop';
const stopPropagation = e => e.stopPropagation();

/**
 * Web variant of `<CenteredDialog>`.
 *
 * A centered modal card rendered into the Bloom Portal at the end of
 * `document.body`. Identical prop surface to native — `visible`, `onClose`,
 * `title`, `compact`, etc. — so call sites are platform agnostic.
 *
 * Differences from the native fork (necessary, not behavioural):
 *   - Portal + `RemoveScrollBar` lock the underlying page while open.
 *   - `Escape` dismisses (when `dismissible`).
 *   - A `role="dialog"` panel with `aria-label`/`aria-modal` for a11y.
 *   - A CSS fade/zoom keyframe matches the native modal's `fade` animation;
 *     the keyframes are injected once (idempotent) into `<head>`.
 *
 * The backdrop is a sibling element BEHIND the card, and the card stops
 * click/press propagation so a tap on the card never dismisses.
 */
function CenteredDialog({
  visible,
  onClose,
  title,
  dismissible = true,
  maxWidth = _centeredDialogTokens.DEFAULT_MAX_WIDTH,
  compact = true,
  showClose,
  children,
  footer,
  accessibilityLabel,
  closeAccessibilityLabel = 'Close dialog',
  backdropAccessibilityLabel = 'Dismiss dialog',
  cardStyle,
  contentStyle,
  testID
}) {
  const theme = (0, _useTheme.useTheme)();
  // `rendered` keeps the dialog mounted through its fade-out so a controlled
  // `visible` flip to `false` still plays an exit animation (matching the
  // native `Modal animationType="fade"`, which animates both directions).
  const [rendered, setRendered] = (0, _react.useState)(visible);
  const isClosing = rendered && !visible;
  useKeyframes();

  // Mount immediately when opened; defer unmount until the exit animation
  // has played when closed.
  (0, _react.useEffect)(() => {
    if (visible) {
      setRendered(true);
      return;
    }
    if (!rendered) return;
    const timer = setTimeout(() => setRendered(false), FADE_OUT_DURATION);
    return () => clearTimeout(timer);
  }, [visible, rendered]);

  // Escape-to-close while open. Scoped to the open lifetime so stacked
  // dialogs don't fight for the keydown — the top-most one wins via
  // document-level event order.
  (0, _react.useEffect)(() => {
    if (!visible || !dismissible || typeof document === 'undefined') return;
    const handler = e => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        onClose();
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [visible, dismissible, onClose]);
  const spacing = (0, _react.useMemo)(() => (0, _centeredDialogTokens.resolveSpacing)(compact), [compact]);
  const resolvedShowClose = showClose ?? Boolean(title);
  const hasHeader = Boolean(title) || resolvedShowClose;
  if (!rendered) return null;

  // While closing, the backdrop must not re-trigger onClose (it's mid-exit).
  const handleBackdropPress = dismissible && !isClosing ? onClose : undefined;
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_indexWeb.Portal, {
    children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactRemoveScrollBar.RemoveScrollBar, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
      style: [styles.root, {
        padding: _centeredDialogTokens.VIEWPORT_GUTTER
      }],
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
        testID: testID ? `${testID}-backdrop` : CENTERED_DIALOG_BACKDROP_TESTID,
        onPress: handleBackdropPress,
        disabled: !dismissible,
        accessibilityRole: "button",
        accessibilityLabel: backdropAccessibilityLabel,
        style: [styles.backdrop, {
          backgroundColor: theme.colors.overlay
        }, backdropAnimation(isClosing)]
      }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
        testID: testID,
        role: "dialog",
        "aria-modal": true,
        "aria-label": accessibilityLabel ?? title,
        onStartShouldSetResponder: returnsTrue,
        onResponderRelease: stopPropagation,
        onClick: stopPropagation,
        style: [styles.card, {
          maxWidth,
          maxHeight: `${_centeredDialogTokens.MAX_HEIGHT_FRACTION * 100}%`,
          backgroundColor: theme.colors.card,
          borderColor: theme.colors.border,
          shadowColor: theme.colors.shadow
        }, cardAnimation(isClosing), cardStyle],
        children: [hasHeader ? /*#__PURE__*/(0, _jsxRuntime.jsx)(DialogHeader, {
          title: title,
          showClose: resolvedShowClose,
          padding: spacing.pad,
          gap: spacing.headerGap,
          closeAccessibilityLabel: closeAccessibilityLabel,
          onClose: onClose
        }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
          style: styles.bodyScroll,
          contentContainerStyle: [{
            padding: spacing.pad,
            paddingTop: hasHeader ? spacing.headerGap : spacing.pad,
            gap: spacing.contentGap
          }, contentStyle],
          showsVerticalScrollIndicator: false,
          keyboardShouldPersistTaps: "handled",
          children: children
        }), footer ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
          style: [styles.footer, {
            padding: spacing.pad,
            paddingTop: spacing.footerGap,
            borderTopColor: theme.colors.borderLight
          }],
          children: footer
        }) : null]
      })]
    })]
  });
}
function DialogHeader({
  title,
  showClose,
  padding,
  gap,
  closeAccessibilityLabel,
  onClose
}) {
  const theme = (0, _useTheme.useTheme)();
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
    style: [styles.header, {
      paddingHorizontal: padding,
      paddingTop: padding,
      gap
    }],
    children: [title ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Text, {
      accessibilityRole: "header",
      numberOfLines: 2,
      style: [styles.title, {
        color: theme.colors.text
      }],
      children: title
    }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
      style: styles.titleSpacer
    }), showClose ? /*#__PURE__*/(0, _jsxRuntime.jsx)(CloseButton, {
      accessibilityLabel: closeAccessibilityLabel,
      onPress: onClose
    }) : null]
  });
}
function CloseButton({
  accessibilityLabel,
  onPress
}) {
  const theme = (0, _useTheme.useTheme)();
  const {
    hovered,
    pressed,
    hoverHandlers,
    pressHandlers
  } = (0, _useInteractionState.useInteractionStates)();
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
    onPress: onPress,
    ...hoverHandlers,
    ...pressHandlers,
    hitSlop: CLOSE_HIT_SLOP,
    accessibilityRole: "button",
    accessibilityLabel: accessibilityLabel,
    style: [styles.closeButton, {
      backgroundColor: theme.colors.contrast50
    }, (hovered || pressed) && styles.closeButtonActive],
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Times.TimesLarge_Stroke2_Corner0_Rounded, {
      size: "sm",
      fill: theme.colors.textSecondary
    })
  });
}
const returnsTrue = () => true;
function backdropAnimation(isClosing) {
  return isClosing ? {
    animation: `bloomCenteredDialogFadeOut ease-in ${FADE_OUT_DURATION}ms forwards`
  } : {
    animation: 'bloomCenteredDialogFadeIn ease-out 150ms'
  };
}
function cardAnimation(isClosing) {
  return isClosing ? {
    animation: `bloomCenteredDialogZoomOut ease-in ${FADE_OUT_DURATION}ms forwards`
  } : {
    animation: 'bloomCenteredDialogZoomIn cubic-bezier(0.16, 1, 0.3, 1) 200ms'
  };
}
const KEYFRAMES_ID = 'bloom-centered-dialog-keyframes';

/**
 * CSS keyframes powering the web enter/exit animation. Injected once into
 * `<head>` on first mount (keyed by id so re-mounts and multiple dialogs
 * don't duplicate the rule). Mirrors the native modal's `fade` feel with a
 * subtle zoom on the card.
 */
const BLOOM_CENTERED_DIALOG_CSS = exports.BLOOM_CENTERED_DIALOG_CSS = `
@keyframes bloomCenteredDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes bloomCenteredDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes bloomCenteredDialogZoomIn { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
@keyframes bloomCenteredDialogZoomOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.96); } }
`;
function useKeyframes() {
  (0, _react.useEffect)(() => {
    if (typeof document === 'undefined') return;
    if (document.getElementById(KEYFRAMES_ID)) return;
    const style = document.createElement('style');
    style.id = KEYFRAMES_ID;
    style.textContent = BLOOM_CENTERED_DIALOG_CSS;
    document.head.appendChild(style);
  }, []);
}
const styles = _reactNative.StyleSheet.create({
  root: {
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: 50,
    alignItems: 'center',
    justifyContent: 'center',
    pointerEvents: 'auto'
  },
  backdrop: {
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  },
  card: {
    position: 'relative',
    width: '100%',
    borderRadius: _centeredDialogTokens.CARD_RADIUS,
    borderWidth: 1,
    overflow: 'hidden',
    shadowOpacity: 0.18,
    shadowRadius: 24,
    shadowOffset: {
      width: 0,
      height: 8
    },
    zIndex: 60
  },
  header: {
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'space-between'
  },
  title: {
    flex: 1,
    fontSize: 17,
    fontWeight: '700',
    lineHeight: 22
  },
  titleSpacer: {
    flex: 1
  },
  closeButton: {
    width: 28,
    height: 28,
    borderRadius: 14,
    alignItems: 'center',
    justifyContent: 'center',
    cursor: 'pointer'
  },
  closeButtonActive: {
    opacity: 0.7
  },
  bodyScroll: {
    flexGrow: 0,
    flexShrink: 1
  },
  footer: {
    borderTopWidth: 1
  }
});
//# sourceMappingURL=CenteredDialog.web.js.map