import { dynamic } from "@solidjs/web";
import { createEffect, createMemo, createSignal, omit, Show } from "solid-js";
import { useDirection } from "../../direction-provider/direction-provider-context.jsx";
import { composeRefs, userRef } from "../../internals/compose-refs.jsx";
import { contains } from "../../internals/dom/contains.jsx";
import { getTarget } from "../../internals/dom/get-target.jsx";
import { mergeRootProps } from "../../internals/merge-root-props.jsx";
import { resolveClassStyle } from "../../internals/resolve-class-style.jsx";
import { useScrollAreaRootContext } from "../root/scroll-area-root-context.jsx";
import { ScrollAreaRootCssVars } from "../root/scroll-area-root-css-vars.jsx";
import { ScrollAreaRootDataAttributes } from "../root/scroll-area-root-data-attributes.jsx";
import { getOffset } from "../utils/get-offset.jsx";
import { ScrollAreaScrollbarContext } from "./scroll-area-scrollbar-context.jsx";
import { ScrollAreaScrollbarCssVars } from "./scroll-area-scrollbar-css-vars.jsx";
import { ScrollAreaScrollbarDataAttributes } from "./scroll-area-scrollbar-data-attributes.jsx";
/**
 * A vertical or horizontal scrollbar. Renders a `<div>` element.
 *
 * Solid 2 notes:
 *
 * - Conditionally rendered via `<Show>` when the scrollbar's axis is hidden
 *   (mirrors React's `shouldRender = keepMounted || !isHidden`).
 * - Wheel handling is added via a native listener (not an `onWheel` prop)
 *   because we need `passive: false` to call `preventDefault`.
 */
export function ScrollAreaScrollbar(props) {
  const context = useScrollAreaRootContext();
  const direction = useDirection();
  const orientation = () => props.orientation ?? "vertical";
  const keepMounted = () => props.keepMounted ?? false;
  const isHidden = () => orientation() === "vertical" ? context.hiddenState().y : context.hiddenState().x;
  const hideTrackUntilMeasured = () => !context.hasMeasuredScrollbar() && !keepMounted();
  const shouldRender = () => keepMounted() || !isHidden();
  const state = createMemo(() => {
    const hidden = context.hiddenState();
    const edges = context.overflowEdges();
    const o = orientation();
    return {
      hovering: context.hovering(),
      scrolling: o === "vertical" ? context.scrollingY() : context.scrollingX(),
      orientation: o,
      hasOverflowX: !hidden.x,
      hasOverflowY: !hidden.y,
      overflowXStart: edges.xStart,
      overflowXEnd: edges.xEnd,
      overflowYStart: edges.yStart,
      overflowYEnd: edges.yEnd,
      cornerHidden: hidden.corner
    };
  });
  const scrollbarContextValue = {
    get orientation() {
      return orientation();
    }
  };

  // Local element signal so the wheel-listener effect retriggers when the
  // scrollbar mounts (context refs are imperative containers, not signals).
  const [scrollbarElSignal, setScrollbarElSignal] = createSignal(null, {
    ownedWrite: true
  });

  // Native wheel listener with `passive: false` so we can call
  // `preventDefault` when the scroll is in-bounds.
  createEffect(() => ({
    render: shouldRender(),
    o: orientation(),
    el: scrollbarElSignal()
  }), ({
    render,
    o,
    el
  }) => {
    if (!render || !el) return;
    const scrollbarEl = el;
    function handleWheel(event) {
      const viewportEl = context.viewportRef.current;
      if (!viewportEl || event.ctrlKey) return;
      event.preventDefault();
      if (o === "vertical") {
        if (viewportEl.scrollTop === 0 && event.deltaY < 0) return;
        if (viewportEl.scrollTop === viewportEl.scrollHeight - viewportEl.clientHeight && event.deltaY > 0) return;
        viewportEl.scrollTop += event.deltaY;
      } else {
        if (viewportEl.scrollLeft === 0 && event.deltaX < 0) return;
        if (viewportEl.scrollLeft === viewportEl.scrollWidth - viewportEl.clientWidth && event.deltaX > 0) return;
        viewportEl.scrollLeft += event.deltaX;
      }
    }
    scrollbarEl.addEventListener("wheel", handleWheel, {
      passive: false
    });
    return () => {
      scrollbarEl.removeEventListener("wheel", handleWheel);
    };
  });
  const userStyling = resolveClassStyle(props, () => state());
  const userProps = omit(props, "class", "style", "attrs", "as", "children", "orientation", "keepMounted");
  const localRef = el => {
    if (orientation() === "vertical") {
      context.scrollbarYRef.current = el;
    } else {
      context.scrollbarXRef.current = el;
    }
    setScrollbarElSignal(el);
  };
  const internalSlot = {
    "data-id": `${context.rootId}-scrollbar`,
    // Inline state-attr getters (replaces the legacy
    // `scrollbarStateAttributesMapping`). `cornerHidden` is
    // intentionally not projected.
    get [ScrollAreaRootDataAttributes.hasOverflowX]() {
      return state().hasOverflowX ? "" : undefined;
    },
    get [ScrollAreaRootDataAttributes.hasOverflowY]() {
      return state().hasOverflowY ? "" : undefined;
    },
    get [ScrollAreaRootDataAttributes.overflowXStart]() {
      return state().overflowXStart ? "" : undefined;
    },
    get [ScrollAreaRootDataAttributes.overflowXEnd]() {
      return state().overflowXEnd ? "" : undefined;
    },
    get [ScrollAreaRootDataAttributes.overflowYStart]() {
      return state().overflowYStart ? "" : undefined;
    },
    get [ScrollAreaRootDataAttributes.overflowYEnd]() {
      return state().overflowYEnd ? "" : undefined;
    },
    get [ScrollAreaScrollbarDataAttributes.hovering]() {
      return state().hovering ? "" : undefined;
    },
    get [ScrollAreaScrollbarDataAttributes.scrolling]() {
      return state().scrolling ? "" : undefined;
    },
    get [ScrollAreaScrollbarDataAttributes.orientation]() {
      return String(state().orientation);
    },
    onPointerDown: event => {
      if (event.button !== 0) return;
      const target = getTarget(event);
      const o = orientation();
      const thumb = o === "vertical" ? context.thumbYRef.current : context.thumbXRef.current;
      // Ignore clicks on the thumb.
      if (thumb && contains(thumb, target)) return;
      const viewportEl = context.viewportRef.current;
      if (!viewportEl) return;
      if (o === "vertical") {
        const thumbYEl = context.thumbYRef.current;
        const scrollbarYEl = context.scrollbarYRef.current;
        if (!thumbYEl || !scrollbarYEl) {
          context.handlePointerDown(event);
          return;
        }
        const thumbYOffset = getOffset(thumbYEl, "margin", "y");
        const scrollbarYOffset = getOffset(scrollbarYEl, "padding", "y");
        const thumbHeight = thumbYEl.offsetHeight;
        const trackRectY = scrollbarYEl.getBoundingClientRect();
        const clickY = event.clientY - trackRectY.top - thumbHeight / 2 - scrollbarYOffset + thumbYOffset / 2;
        const scrollableContentHeight = viewportEl.scrollHeight;
        const viewportHeight = viewportEl.clientHeight;
        const maxThumbOffsetY = scrollbarYEl.offsetHeight - thumbHeight - scrollbarYOffset - thumbYOffset;
        const scrollRatioY = clickY / maxThumbOffsetY;
        viewportEl.scrollTop = scrollRatioY * (scrollableContentHeight - viewportHeight);
      } else {
        const thumbXEl = context.thumbXRef.current;
        const scrollbarXEl = context.scrollbarXRef.current;
        if (!thumbXEl || !scrollbarXEl) {
          context.handlePointerDown(event);
          return;
        }
        const thumbXOffset = getOffset(thumbXEl, "margin", "x");
        const scrollbarXOffset = getOffset(scrollbarXEl, "padding", "x");
        const thumbWidth = thumbXEl.offsetWidth;
        const trackRectX = scrollbarXEl.getBoundingClientRect();
        const clickX = event.clientX - trackRectX.left - thumbWidth / 2 - scrollbarXOffset + thumbXOffset / 2;
        const scrollableContentWidth = viewportEl.scrollWidth;
        const viewportWidth = viewportEl.clientWidth;
        const maxThumbOffsetX = scrollbarXEl.offsetWidth - thumbWidth - scrollbarXOffset - thumbXOffset;
        const scrollRatioX = clickX / maxThumbOffsetX;
        let newScrollLeft;
        if (direction() === "rtl") {
          newScrollLeft = (1 - scrollRatioX) * (scrollableContentWidth - viewportWidth);
          if (viewportEl.scrollLeft <= 0) {
            newScrollLeft = -newScrollLeft;
          }
        } else {
          newScrollLeft = scrollRatioX * (scrollableContentWidth - viewportWidth);
        }
        viewportEl.scrollLeft = newScrollLeft;
      }
      context.handlePointerDown(event);
    },
    onPointerUp: event => {
      context.handlePointerUp(event);
    },
    get style() {
      const o = orientation();
      const thumb = context.thumbSize();
      const base = {
        position: "absolute",
        "touch-action": "none",
        "-webkit-user-select": "none",
        "user-select": "none"
      };
      if (hideTrackUntilMeasured()) {
        base.visibility = "hidden";
      }
      if (o === "vertical") {
        base.top = 0;
        base.bottom = `var(${ScrollAreaRootCssVars.scrollAreaCornerHeight})`;
        base["inset-inline-end"] = 0;
        base[ScrollAreaScrollbarCssVars.scrollAreaThumbHeight] = `${thumb.height}px`;
      } else {
        base["inset-inline-start"] = 0;
        base["inset-inline-end"] = `var(${ScrollAreaRootCssVars.scrollAreaCornerWidth})`;
        base.bottom = 0;
        base[ScrollAreaScrollbarCssVars.scrollAreaThumbWidth] = `${thumb.width}px`;
      }
      return base;
    },
    get children() {
      return <ScrollAreaScrollbarContext value={scrollbarContextValue}>
					{props.children}
				</ScrollAreaScrollbarContext>;
    }
  };
  const styledUserProps = mergeRootProps(userStyling, userProps);
  const merged = mergeRootProps(internalSlot, styledUserProps);
  const Tag = dynamic(() => props.as ?? "div");
  return <Show when={shouldRender()}>
			<Tag {...merged} ref={composeRefs(localRef, userRef(props))} />
		</Show>;
}
//# sourceMappingURL=scroll-area-scrollbar.jsx.map
