"use client";
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
import { cx, getStyles } from "../../../styles/styler.js";
import { Box } from "../../Box.js";
import { Scrim } from "../../Scrim.js";
import { BottomSheetHandle } from "./BottomSheetHandle.js";
import { BottomSheetInternalContext } from "./BottomSheetInternalContext.js";
import { useBottomSheetConfig } from "./UDSBottomSheetConfigProvider.js";
import { useBottomSheetDrag } from "./useBottomSheetDrag.js";
import { useBottomSheetSnapModel } from "./useBottomSheetSnapModel.js";
import { getBottomSheetInternal } from "./useBottomSheetStore.js";
import { useExpansionMargins } from "./useExpansionMargins.js";
import { useViewportHeight } from "./useViewportHeight.js";
import { useVirtualKeyboard } from "./useVirtualKeyboard.js";
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { Dialog, useDialogContext } from "@ariakit/react";

//#region src/components/client/BottomSheet/BottomSheet.tsx
/**
* Drag-to-dismiss distance threshold as a ratio of the lowest snap point's
* visible height. Dragging down past this fraction of the height dismisses
* the sheet (regardless of velocity).
*
* Velocity and distance are OR conditions — exceeding either one triggers dismissal.
*/
const DRAG_DISMISS_THRESHOLD_RATIO = .25;
/**
* Top inset reserved when calculating max sheet height.
* Keeps a visible sliver of page content so the fully expanded state still reads as a bottom sheet.
*/
const SHEET_MAX_HEIGHT_TOP_OFFSET_PX = 26;
function BottomSheet({ children, className: sheetClassName, controller: controllerProp, variant = "default", snapPoints: snapPointsProp, defaultSnapPointIndex = 0, snapPointIndex: snapPointIndexProp, onSnapPointChange, height, modal = true, portal = true, portalElement, dismissible = true, enableDrag = true, showHandleIndicator = true, hideOnEscape = true, onClose, backdrop, preventBodyScroll, slotProps, fullWidthAtMaxSnap = false }) {
	const storeFromContext = useDialogContext();
	const dialogStore = controllerProp ? getBottomSheetInternal(controllerProp) : storeFromContext;
	const { backgroundColor: configBackgroundColor } = useBottomSheetConfig();
	if (!dialogStore) throw new Error("BottomSheet must be wrapped in BottomSheetProvider or provided a controller prop.");
	const isOpen = dialogStore.useState().open;
	const keyboardHeightPx = useVirtualKeyboard(isOpen);
	const viewportHeightPx = useViewportHeight();
	const isDraggingRef = useRef(false);
	const forceZeroMarginsRef = useRef(false);
	forceZeroMarginsRef.current = keyboardHeightPx > 0;
	const sheetRef = useRef(null);
	const maxSnapPxRef = useRef(0);
	const sheetHeightRef = useRef(void 0);
	const wasOpenRef = useRef(false);
	if (isOpen && !wasOpenRef.current) sheetHeightRef.current = void 0;
	wasOpenRef.current = isOpen;
	const maxHeightPx = Math.max(viewportHeightPx - SHEET_MAX_HEIGHT_TOP_OFFSET_PX, 0);
	const applySheetHeight = useCallback((el, h) => {
		el.style.height = h;
		sheetHeightRef.current = h;
	}, []);
	const rootVariantClass = getStyles({ bottomsheetVariantRoot: variant });
	const { applyExpansionMargins, thresholdRef: expansionThresholdRef } = useExpansionMargins({
		sheetRef,
		maxSnapPxRef,
		fullWidthAtMaxSnap,
		snapPointsLength: snapPointsProp?.length ?? 1,
		isOpen,
		forceZeroRef: forceZeroMarginsRef
	});
	/**
	* Applies drag-time position updates with no animation.
	* We model the sheet by height, so translate is converted to height:
	* `height = maxSnapPointPx - translatePx`.
	*/
	const setTranslateImmediate = useCallback((nextTranslatePx) => {
		const el = sheetRef.current;
		if (!el) return;
		applySheetHeight(el, `${maxSnapPxRef.current - nextTranslatePx}px`);
		applyExpansionMargins(el, nextTranslatePx);
	}, [applySheetHeight, applyExpansionMargins]);
	const { resolvedSnapPoints, snapPointsPx, maxSnapPointPx, snapPointTranslatesPx, activeSnapIndex, restTranslatePx, emitSnapPointChange, openTimeRef, lastTimeDragPreventedRef, isEnteringRef } = useBottomSheetSnapModel({
		isOpen,
		snapPointsProp,
		height,
		defaultSnapPointIndex,
		snapPointIndexProp,
		onSnapPointChange,
		viewportHeightPx,
		maxHeightPx,
		setTranslateAnimated: useCallback((nextTranslatePx) => {
			const el = sheetRef.current;
			if (!el) return;
			const h = `${maxSnapPxRef.current - nextTranslatePx}px`;
			if (!el.style.height) {
				applySheetHeight(el, h);
				applyExpansionMargins(el, nextTranslatePx);
			} else {
				const easing = "cubic-bezier(0.32, 0.72, 0, 1)";
				let transition = `height 500ms ${easing}`;
				if (expansionThresholdRef.current != null) transition += `, margin-left 500ms ${easing}, margin-right 500ms ${easing}, margin-bottom 500ms ${easing}`;
				el.style.transition = transition;
				applySheetHeight(el, h);
				applyExpansionMargins(el, nextTranslatePx);
				const cleanup = () => {
					el.style.removeProperty("transition");
					el.removeEventListener("transitionend", cleanup);
				};
				el.addEventListener("transitionend", cleanup);
			}
		}, [
			applySheetHeight,
			applyExpansionMargins,
			expansionThresholdRef
		]),
		isDraggingRef
	});
	maxSnapPxRef.current = maxSnapPointPx;
	expansionThresholdRef.current = fullWidthAtMaxSnap && (snapPointsProp?.length ?? 1) >= 2 ? snapPointsPx[snapPointsPx.length - 2] ?? 0 : null;
	const visibleTranslatePx = isOpen && !isEnteringRef.current ? 0 : viewportHeightPx;
	const resolvedDismissThresholdPx = (snapPointsPx[0] ?? maxSnapPointPx) * DRAG_DISMISS_THRESHOLD_RATIO;
	const shouldShowHandleIndicator = showHandleIndicator && enableDrag;
	const isAtMaxSnapPoint = activeSnapIndex === resolvedSnapPoints.length - 1;
	const scrollLocked = !isAtMaxSnapPoint;
	const internalContextValue = useMemo(() => ({ scrollLocked }), [scrollLocked]);
	const shouldHideEdgeBordersAtMaxSnap = fullWidthAtMaxSnap && resolvedSnapPoints.length >= 2 && isAtMaxSnapPoint;
	const handleHandleIndicatorKeyDown = useCallback((event) => {
		if (!shouldShowHandleIndicator || resolvedSnapPoints.length <= 1) return;
		if (event.key === "ArrowUp") {
			event.preventDefault();
			emitSnapPointChange(activeSnapIndex + 1);
		}
		if (event.key === "ArrowDown") {
			event.preventDefault();
			if (activeSnapIndex === 0 && dismissible) {
				dialogStore.hide();
				return;
			}
			emitSnapPointChange(activeSnapIndex - 1);
		}
	}, [
		activeSnapIndex,
		dismissible,
		emitSnapPointChange,
		resolvedSnapPoints.length,
		shouldShowHandleIndicator,
		dialogStore
	]);
	const { onPointerDown, onPointerMove, onPointerUp, onPointerCancel } = useBottomSheetDrag({
		enableDrag,
		dismissible,
		store: dialogStore,
		sheetRef,
		isDraggingRef,
		activeSnapIndex,
		snapPointTranslatesPx,
		maxSnapPointPx,
		resolvedSnapPointsLength: resolvedSnapPoints.length,
		resolvedDismissThresholdPx,
		emitSnapPointChange,
		setTranslateImmediate,
		openTimeRef,
		lastTimeDragPreventedRef
	});
	useEffect(() => {
		if (!fullWidthAtMaxSnap || !isOpen) return;
		const el = sheetRef.current;
		if (!el || !el.style.height) return;
		applyExpansionMargins(el, restTranslatePx);
	}, [
		restTranslatePx,
		fullWidthAtMaxSnap,
		isOpen,
		applyExpansionMargins
	]);
	useLayoutEffect(() => {
		const el = sheetRef.current;
		if (!el || !sheetHeightRef.current) return;
		if (!el.style.height) el.style.height = sheetHeightRef.current;
	});
	const dialogBackdrop = modal ? backdrop ?? /* @__PURE__ */ jsx(Scrim, {
		className: slotProps?.scrim?.className,
		animateOpacity: true
	}) : false;
	const hideOnInteractOutside = modal && dismissible ? void 0 : false;
	const isElevationBackgroundColor = configBackgroundColor[variant].startsWith("elevation-");
	return /* @__PURE__ */ jsxs(Dialog, {
		ref: sheetRef,
		store: dialogStore,
		modal,
		unmountOnHide: true,
		hideOnEscape: dismissible ? hideOnEscape : false,
		hideOnInteractOutside,
		onClose,
		backdrop: dialogBackdrop,
		portal,
		portalElement,
		preventBodyScroll: preventBodyScroll ?? (modal ? void 0 : false),
		"data-testid": "bottom-sheet",
		className: cx("fixed overflow-hidden inset-x-0 bottom-0 z-50", keyboardHeightPx > 0 ? void 0 : "bottom-0", scrollLocked ? "[will-change:transform] touch-none" : "[will-change:transform]", "[transform:translate3d(0,var(--uds-bottomsheet-hidden-translate),0)]", "data-[enter]:[transform:translate3d(0,var(--uds-bottomsheet-visible-translate),0)]", "transition-transform duration-500 ease-[cubic-bezier(0.32,0.72,0,1)]", "motion-reduce:transition-none", sheetClassName, rootVariantClass),
		style: {
			height: sheetHeightRef.current,
			maxHeight: maxHeightPx ? `${maxHeightPx}px` : void 0,
			...shouldHideEdgeBordersAtMaxSnap ? {
				borderBottomWidth: 0,
				borderLeftWidth: 0,
				borderRightWidth: 0
			} : {},
			["--uds-bottomsheet-hidden-translate"]: `${viewportHeightPx}px`,
			["--uds-bottomsheet-visible-translate"]: `${visibleTranslatePx}px`,
			...isElevationBackgroundColor && {
				backdropFilter: `var(--uds-background-blur-radius-${configBackgroundColor[variant]})`,
				WebkitBackdropFilter: `var(--uds-background-blur-radius-${configBackgroundColor[variant]})`
			}
		},
		onPointerDown,
		onPointerMove,
		onPointerUp,
		onPointerCancel,
		children: [shouldShowHandleIndicator && /* @__PURE__ */ jsx(Box, {
			display: "block",
			position: "absolute",
			className: cx("inset-x-0 top-[-4px] z-[1] touch-pan-y"),
			children: /* @__PURE__ */ jsx(BottomSheetHandle, {
				onKeyDown: handleHandleIndicatorKeyDown,
				ariaLabel: "Resize sheet",
				className: slotProps?.handleIndicator?.className,
				variant
			})
		}), /* @__PURE__ */ jsx(BottomSheetInternalContext.Provider, {
			value: internalContextValue,
			children: /* @__PURE__ */ jsx(Box, {
				display: "flex",
				flexDirection: "column",
				className: "absolute inset-0 min-h-0 p-[inherit]",
				children
			})
		})]
	});
}
BottomSheet.displayName = "BottomSheet";

//#endregion
export { BottomSheet };