"use client";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { useStableCallback } from "../../../../utils/esm/useStableCallback.js";
import { useIsoLayoutEffect } from "../../../../utils/esm/useIsoLayoutEffect.js";
import { isWebKit } from "../../../../utils/esm/detectBrowser.js";
import { useTimeout } from "../../../../utils/esm/useTimeout.js";
import { useScrollAreaRootContext } from "../root/ScrollAreaRootContext.js";
import { ScrollAreaViewportContext } from "./ScrollAreaViewportContext.js";
import { useRenderElement } from "../../utils/useRenderElement.js";
import { useDirection } from "../../direction-provider/DirectionContext.js";
import { getOffset } from "../utils/getOffset.js";
import { MIN_THUMB_SIZE } from "../constants.js";
import { clamp } from "../../utils/clamp.js";
import { styleDisableScrollbar } from "../../utils/styles.js";
import { onVisible } from "../utils/onVisible.js";
import { scrollAreaStateAttributesMapping } from "../root/stateAttributes.js";
import { ScrollAreaViewportCssVars } from "./ScrollAreaViewportCssVars.js";
import { jsx } from "react/jsx-runtime";
let scrollAreaOverflowVarsRegistered = false;
function removeCSSVariableInheritance() {
  if (scrollAreaOverflowVarsRegistered || // When `inherits: false`, specifying `inherit` on child elements doesn't work
  // in Safari. To let CSS features work correctly, this optimization must be skipped.
  isWebKit) {
    return;
  }
  if (typeof CSS !== "undefined" && "registerProperty" in CSS) {
    [ScrollAreaViewportCssVars.scrollAreaOverflowXStart, ScrollAreaViewportCssVars.scrollAreaOverflowXEnd, ScrollAreaViewportCssVars.scrollAreaOverflowYStart, ScrollAreaViewportCssVars.scrollAreaOverflowYEnd].forEach((name) => {
      try {
        CSS.registerProperty({
          name,
          syntax: "<length>",
          inherits: false,
          initialValue: "0px"
        });
      } catch {
      }
    });
  }
  scrollAreaOverflowVarsRegistered = true;
}
const ScrollAreaViewport = /* @__PURE__ */ React.forwardRef(function ScrollAreaViewport2(componentProps, forwardedRef) {
  const {
    render,
    className,
    ...elementProps
  } = componentProps;
  const {
    viewportRef,
    scrollbarYRef,
    scrollbarXRef,
    thumbYRef,
    thumbXRef,
    cornerRef,
    setCornerSize,
    setThumbSize,
    rootId,
    setHiddenState,
    hiddenState,
    handleScroll,
    setHovering,
    setOverflowEdges,
    overflowEdges,
    overflowEdgeThreshold
  } = useScrollAreaRootContext();
  const direction = useDirection();
  const programmaticScrollRef = React.useRef(true);
  const scrollEndTimeout = useTimeout();
  const waitForAnimationsTimeout = useTimeout();
  function computeThumbPositionHandler() {
    const viewportEl = viewportRef.current;
    const scrollbarYEl = scrollbarYRef.current;
    const scrollbarXEl = scrollbarXRef.current;
    const thumbYEl = thumbYRef.current;
    const thumbXEl = thumbXRef.current;
    const cornerEl = cornerRef.current;
    if (!viewportEl) {
      return;
    }
    const scrollableContentHeight = viewportEl.scrollHeight;
    const scrollableContentWidth = viewportEl.scrollWidth;
    const viewportHeight = viewportEl.clientHeight;
    const viewportWidth = viewportEl.clientWidth;
    const scrollTop = viewportEl.scrollTop;
    const scrollLeft = viewportEl.scrollLeft;
    if (scrollableContentHeight === 0 || scrollableContentWidth === 0) {
      return;
    }
    const scrollbarYHidden = viewportHeight >= scrollableContentHeight;
    const scrollbarXHidden = viewportWidth >= scrollableContentWidth;
    const ratioX = viewportWidth / scrollableContentWidth;
    const ratioY = viewportHeight / scrollableContentHeight;
    const maxScrollLeft = Math.max(0, scrollableContentWidth - viewportWidth);
    const maxScrollTop = Math.max(0, scrollableContentHeight - viewportHeight);
    let scrollLeftFromStart = 0;
    let scrollLeftFromEnd = 0;
    if (!scrollbarXHidden) {
      if (direction === "rtl") {
        scrollLeftFromStart = clamp(-scrollLeft, 0, maxScrollLeft);
      } else {
        scrollLeftFromStart = clamp(scrollLeft, 0, maxScrollLeft);
      }
      scrollLeftFromEnd = maxScrollLeft - scrollLeftFromStart;
    }
    const scrollTopFromStart = !scrollbarYHidden ? clamp(scrollTop, 0, maxScrollTop) : 0;
    const scrollTopFromEnd = !scrollbarYHidden ? maxScrollTop - scrollTopFromStart : 0;
    const nextWidth = scrollbarXHidden ? 0 : viewportWidth;
    const nextHeight = scrollbarYHidden ? 0 : viewportHeight;
    const scrollbarXOffset = getOffset(scrollbarXEl, "padding", "x");
    const scrollbarYOffset = getOffset(scrollbarYEl, "padding", "y");
    const thumbXOffset = getOffset(thumbXEl, "margin", "x");
    const thumbYOffset = getOffset(thumbYEl, "margin", "y");
    const idealNextWidth = nextWidth - scrollbarXOffset - thumbXOffset;
    const idealNextHeight = nextHeight - scrollbarYOffset - thumbYOffset;
    const maxNextWidth = scrollbarXEl ? Math.min(scrollbarXEl.offsetWidth, idealNextWidth) : idealNextWidth;
    const maxNextHeight = scrollbarYEl ? Math.min(scrollbarYEl.offsetHeight, idealNextHeight) : idealNextHeight;
    const clampedNextWidth = Math.max(MIN_THUMB_SIZE, maxNextWidth * ratioX);
    const clampedNextHeight = Math.max(MIN_THUMB_SIZE, maxNextHeight * ratioY);
    setThumbSize((prevSize) => {
      if (prevSize.height === clampedNextHeight && prevSize.width === clampedNextWidth) {
        return prevSize;
      }
      return {
        width: clampedNextWidth,
        height: clampedNextHeight
      };
    });
    if (scrollbarYEl && thumbYEl) {
      const maxThumbOffsetY = scrollbarYEl.offsetHeight - clampedNextHeight - scrollbarYOffset - thumbYOffset;
      const scrollRangeY = scrollableContentHeight - viewportHeight;
      const scrollRatioY = scrollRangeY === 0 ? 0 : scrollTop / scrollRangeY;
      const thumbOffsetY = Math.min(maxThumbOffsetY, Math.max(0, scrollRatioY * maxThumbOffsetY));
      thumbYEl.style.transform = `translate3d(0,${thumbOffsetY}px,0)`;
    }
    if (scrollbarXEl && thumbXEl) {
      const maxThumbOffsetX = scrollbarXEl.offsetWidth - clampedNextWidth - scrollbarXOffset - thumbXOffset;
      const scrollRangeX = scrollableContentWidth - viewportWidth;
      const scrollRatioX = scrollRangeX === 0 ? 0 : scrollLeft / scrollRangeX;
      const thumbOffsetX = direction === "rtl" ? clamp(scrollRatioX * maxThumbOffsetX, -maxThumbOffsetX, 0) : clamp(scrollRatioX * maxThumbOffsetX, 0, maxThumbOffsetX);
      thumbXEl.style.transform = `translate3d(${thumbOffsetX}px,0,0)`;
    }
    const clampedScrollLeftStart = clamp(scrollLeftFromStart, 0, maxScrollLeft);
    const clampedScrollLeftEnd = clamp(scrollLeftFromEnd, 0, maxScrollLeft);
    const clampedScrollTopStart = clamp(scrollTopFromStart, 0, maxScrollTop);
    const clampedScrollTopEnd = clamp(scrollTopFromEnd, 0, maxScrollTop);
    const overflowMetricsPx = [[ScrollAreaViewportCssVars.scrollAreaOverflowXStart, clampedScrollLeftStart], [ScrollAreaViewportCssVars.scrollAreaOverflowXEnd, clampedScrollLeftEnd], [ScrollAreaViewportCssVars.scrollAreaOverflowYStart, clampedScrollTopStart], [ScrollAreaViewportCssVars.scrollAreaOverflowYEnd, clampedScrollTopEnd]];
    for (const [cssVar, value] of overflowMetricsPx) {
      viewportEl.style.setProperty(cssVar, `${value}px`);
    }
    if (cornerEl) {
      if (scrollbarXHidden || scrollbarYHidden) {
        setCornerSize({
          width: 0,
          height: 0
        });
      } else if (!scrollbarXHidden && !scrollbarYHidden) {
        const width = scrollbarYEl?.offsetWidth || 0;
        const height = scrollbarXEl?.offsetHeight || 0;
        setCornerSize({
          width,
          height
        });
      }
    }
    setHiddenState((prevState) => {
      const cornerHidden = scrollbarYHidden || scrollbarXHidden;
      if (prevState.scrollbarYHidden === scrollbarYHidden && prevState.scrollbarXHidden === scrollbarXHidden && prevState.cornerHidden === cornerHidden) {
        return prevState;
      }
      return {
        scrollbarYHidden,
        scrollbarXHidden,
        cornerHidden
      };
    });
    const nextOverflowEdges = {
      xStart: !scrollbarXHidden && clampedScrollLeftStart > overflowEdgeThreshold.xStart,
      xEnd: !scrollbarXHidden && clampedScrollLeftEnd > overflowEdgeThreshold.xEnd,
      yStart: !scrollbarYHidden && clampedScrollTopStart > overflowEdgeThreshold.yStart,
      yEnd: !scrollbarYHidden && clampedScrollTopEnd > overflowEdgeThreshold.yEnd
    };
    setOverflowEdges((prev) => {
      if (prev.xStart === nextOverflowEdges.xStart && prev.xEnd === nextOverflowEdges.xEnd && prev.yStart === nextOverflowEdges.yStart && prev.yEnd === nextOverflowEdges.yEnd) {
        return prev;
      }
      return nextOverflowEdges;
    });
  }
  const computeThumbPosition = useStableCallback(() => {
    ReactDOM.flushSync(computeThumbPositionHandler);
  });
  useIsoLayoutEffect(() => {
    if (!viewportRef.current) {
      return void 0;
    }
    removeCSSVariableInheritance();
    const cleanup = onVisible(viewportRef.current, computeThumbPosition);
    return cleanup;
  }, [computeThumbPosition, viewportRef]);
  useIsoLayoutEffect(() => {
    queueMicrotask(computeThumbPosition);
  }, [computeThumbPosition, hiddenState, direction]);
  useIsoLayoutEffect(() => {
    if (viewportRef.current?.matches(":hover")) {
      setHovering(true);
    }
  }, [viewportRef, setHovering]);
  React.useEffect(() => {
    const viewport = viewportRef.current;
    if (typeof ResizeObserver === "undefined" || !viewport) {
      return void 0;
    }
    const ro = new ResizeObserver(computeThumbPosition);
    ro.observe(viewport);
    waitForAnimationsTimeout.start(0, () => {
      Promise.all(viewport.getAnimations({
        subtree: true
      }).map((animation) => animation.finished)).then(computeThumbPosition).catch(() => {
      });
    });
    return () => {
      ro.disconnect();
      waitForAnimationsTimeout.clear();
    };
  }, [computeThumbPosition, viewportRef, waitForAnimationsTimeout]);
  function handleUserInteraction() {
    programmaticScrollRef.current = false;
  }
  const props = {
    role: "presentation",
    ...rootId && {
      "data-id": `${rootId}-viewport`
    },
    // https://accessibilityinsights.io/info-examples/web/scrollable-region-focusable/
    ...(!hiddenState.scrollbarXHidden || !hiddenState.scrollbarYHidden) && {
      tabIndex: 0
    },
    className: styleDisableScrollbar.className,
    style: {
      overflow: "scroll"
    },
    onScroll() {
      if (!viewportRef.current) {
        return;
      }
      computeThumbPosition();
      if (!programmaticScrollRef.current) {
        handleScroll({
          x: viewportRef.current.scrollLeft,
          y: viewportRef.current.scrollTop
        });
      }
      scrollEndTimeout.start(100, () => {
        programmaticScrollRef.current = true;
      });
    },
    onWheel: handleUserInteraction,
    onTouchMove: handleUserInteraction,
    onPointerMove: handleUserInteraction,
    onPointerEnter: handleUserInteraction,
    onKeyDown: handleUserInteraction
  };
  const viewportState = React.useMemo(() => ({
    hasOverflowX: !hiddenState.scrollbarXHidden,
    hasOverflowY: !hiddenState.scrollbarYHidden,
    overflowXStart: overflowEdges.xStart,
    overflowXEnd: overflowEdges.xEnd,
    overflowYStart: overflowEdges.yStart,
    overflowYEnd: overflowEdges.yEnd,
    cornerHidden: hiddenState.cornerHidden
  }), [hiddenState.scrollbarXHidden, hiddenState.scrollbarYHidden, hiddenState.cornerHidden, overflowEdges]);
  const element = useRenderElement("div", componentProps, {
    ref: [forwardedRef, viewportRef],
    state: viewportState,
    props: [props, elementProps],
    stateAttributesMapping: scrollAreaStateAttributesMapping
  });
  const contextValue = React.useMemo(() => ({
    computeThumbPosition
  }), [computeThumbPosition]);
  return /* @__PURE__ */ jsx(ScrollAreaViewportContext.Provider, {
    value: contextValue,
    children: element
  });
});
if (process.env.NODE_ENV !== "production") ScrollAreaViewport.displayName = "ScrollAreaViewport";
export {
  ScrollAreaViewport
};
//# sourceMappingURL=ScrollAreaViewport.js.map
