// src/ScrollView.tsx
import {
  useState as useState2,
  useRef as useRef2,
  forwardRef as forwardRef2,
  useImperativeHandle as useImperativeHandle2,
  useCallback as useCallback2
} from "react";

// src/ControlledScrollView.tsx
import {
  useState,
  useRef,
  forwardRef,
  useImperativeHandle,
  useLayoutEffect,
  useCallback,
  Children,
  isValidElement
} from "react";
import { Box, measureElement } from "ink";
import { jsx } from "react/jsx-runtime";
var MeasurableItem = ({
  children,
  onMeasure,
  index,
  width,
  measureKey
}) => {
  const ref = useRef(null);
  useLayoutEffect(() => {
    if (ref.current) {
      const { height } = measureElement(ref.current);
      onMeasure(index, height);
    }
  }, [index, onMeasure, width, measureKey, children]);
  return /* @__PURE__ */ jsx(Box, { ref, flexShrink: 0, width: "100%", flexDirection: "column", children });
};
function useStateRef(initialValue) {
  const [state, setStateInternal] = useState(initialValue);
  const ref = useRef(initialValue);
  const setState = useCallback((update) => {
    const nextValue = typeof update === "function" ? update(ref.current) : update;
    ref.current = nextValue;
    setStateInternal(nextValue);
  }, []);
  const getState = useCallback(() => ref.current, []);
  return [state, setState, getState];
}
var ControlledScrollView = forwardRef(
  ({
    scrollOffset,
    onViewportSizeChange,
    onContentHeightChange,
    onItemHeightChange,
    debug = false,
    children,
    ...boxProps
  }, ref) => {
    const [viewportSize, setViewportSize, getViewportSize] = useStateRef({
      height: 0,
      width: 0
    });
    const [contentHeight, setContentHeight, getContentHeight] = useStateRef(0);
    const [itemMeasureKeys, setItemMeasureKeys] = useState({});
    const viewportRef = useRef(null);
    const contentRef = useRef(null);
    const prevContentHeightRef = useRef(0);
    useLayoutEffect(() => {
      if (contentHeight !== prevContentHeightRef.current) {
        onContentHeightChange?.(contentHeight, prevContentHeightRef.current);
        prevContentHeightRef.current = contentHeight;
      }
    }, [contentHeight, onContentHeightChange]);
    const itemHeightsRef = useRef({});
    const itemKeysRef = useRef([]);
    const itemOffsetsRef = useRef([]);
    const firstInvalidOffsetIndexRef = useRef(0);
    const handleItemMeasure = useCallback(
      (index, height) => {
        const key = itemKeysRef.current[index] || index;
        if (itemHeightsRef.current[key] !== height) {
          const previousHeight = itemHeightsRef.current[key] || 0;
          itemHeightsRef.current = {
            ...itemHeightsRef.current,
            [key]: height
          };
          let newTotalHeight = 0;
          for (const itemKey of itemKeysRef.current) {
            newTotalHeight += itemHeightsRef.current[itemKey] || 0;
          }
          const currentHeight = getContentHeight();
          if (newTotalHeight !== currentHeight) {
            setContentHeight(newTotalHeight);
          }
          onItemHeightChange?.(index, height, previousHeight);
          firstInvalidOffsetIndexRef.current = Math.min(
            firstInvalidOffsetIndexRef.current,
            index + 1
          );
        }
      },
      [
        onItemHeightChange,
        onContentHeightChange,
        getContentHeight,
        setContentHeight
      ]
    );
    const measureViewport = useCallback(() => {
      if (viewportRef.current) {
        const { width, height } = measureElement(viewportRef.current);
        const currentSize = getViewportSize();
        if (width !== currentSize.width || height !== currentSize.height) {
          onViewportSizeChange?.({ width, height }, currentSize);
          setViewportSize({ width, height });
        }
      }
    }, [viewportRef, onViewportSizeChange, getViewportSize, setViewportSize]);
    useLayoutEffect(() => {
      measureViewport();
    });
    const prevChildrenRef = useRef(null);
    if (prevChildrenRef.current !== children) {
      prevChildrenRef.current = children;
      const newItemKeys = [];
      const newItemHeights = {};
      Children.forEach(children, (child, index) => {
        if (!child) return;
        const key = isValidElement(child) ? child.key : null;
        const effectiveKey = key !== null ? key : index;
        newItemKeys[index] = effectiveKey;
        const itemHeight = itemHeightsRef.current[effectiveKey] || 0;
        newItemHeights[effectiveKey] = itemHeight;
      });
      itemHeightsRef.current = newItemHeights;
      itemKeysRef.current = newItemKeys;
      itemOffsetsRef.current = new Array(newItemKeys.length).fill(0);
      firstInvalidOffsetIndexRef.current = 0;
      let newTotalHeight = 0;
      newItemKeys.forEach((itemKey) => {
        newTotalHeight += newItemHeights[itemKey] || 0;
      });
      const currentHeight = getContentHeight();
      if (newTotalHeight !== currentHeight) {
        setContentHeight(newTotalHeight);
      }
    }
    useImperativeHandle(
      ref,
      () => ({
        getContentHeight,
        getViewportHeight: () => getViewportSize().height,
        getBottomOffset: () => Math.max(0, getContentHeight() - getViewportSize().height),
        getItemHeight: (index) => {
          const key = itemKeysRef.current[index] || index;
          return itemHeightsRef.current[key] || 0;
        },
        remeasure: measureViewport,
        remeasureItem: (index) => setItemMeasureKeys((prev) => ({
          ...prev,
          [index]: (prev[index] || 0) + 1
        })),
        getItemPosition: (index) => {
          if (index < 0 || index >= itemKeysRef.current.length) {
            return null;
          }
          if (index >= firstInvalidOffsetIndexRef.current) {
            let currentOffset = 0;
            let startIndex = 0;
            if (firstInvalidOffsetIndexRef.current > 0) {
              startIndex = firstInvalidOffsetIndexRef.current;
              const prevIndex = startIndex - 1;
              const prevKey = itemKeysRef.current[prevIndex] || prevIndex;
              const prevHeight = itemHeightsRef.current[prevKey] || 0;
              currentOffset = (itemOffsetsRef.current[prevIndex] ?? 0) + prevHeight;
            }
            for (let i = startIndex; i <= index; i++) {
              itemOffsetsRef.current[i] = currentOffset;
              const key2 = itemKeysRef.current[i] || i;
              const height2 = itemHeightsRef.current[key2] || 0;
              currentOffset += height2;
            }
            firstInvalidOffsetIndexRef.current = index + 1;
          }
          const top = itemOffsetsRef.current[index] ?? 0;
          const key = itemKeysRef.current[index] || index;
          const height = itemHeightsRef.current[key] || 0;
          return { top, height };
        }
      }),
      []
    );
    return /* @__PURE__ */ jsx(Box, { ...boxProps, children: /* @__PURE__ */ jsx(Box, { ref: viewportRef, width: "100%", children: /* @__PURE__ */ jsx(Box, { overflow: debug ? void 0 : "hidden", width: "100%", children: /* @__PURE__ */ jsx(
      Box,
      {
        ref: contentRef,
        width: "100%",
        flexDirection: "column",
        marginTop: -scrollOffset,
        children: Children.map(children, (child, index) => {
          if (!child) return null;
          return /* @__PURE__ */ jsx(
            MeasurableItem,
            {
              index,
              width: viewportSize.width,
              onMeasure: handleItemMeasure,
              measureKey: itemMeasureKeys[index],
              children: child
            },
            isValidElement(child) ? child.key || index : index
          );
        })
      }
    ) }) }) });
  }
);

// src/ScrollView.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
function useStateRef2(initialValue) {
  const [state, setStateInternal] = useState2(initialValue);
  const ref = useRef2(initialValue);
  const setState = useCallback2((update) => {
    const nextValue = typeof update === "function" ? update(ref.current) : update;
    ref.current = nextValue;
    setStateInternal(nextValue);
  }, []);
  const getState = useCallback2(() => ref.current, []);
  return [state, setState, getState];
}
var ScrollView = forwardRef2(
  ({
    onScroll,
    onViewportSizeChange,
    onContentHeightChange,
    onItemHeightChange,
    debug = false,
    children,
    ...boxProps
  }, ref) => {
    const [scrollOffset, setScrollOffset, getScrollOffset] = useStateRef2(0);
    const innerRef = useRef2(null);
    const contentHeightRef = useRef2(0);
    const handleContentHeightChange = useCallback2(
      (height, previousHeight) => {
        contentHeightRef.current = height;
        onContentHeightChange?.(height, previousHeight);
        if (getScrollOffset() > height) {
          setScrollOffset(height);
          onScroll?.(height);
        }
      },
      [onContentHeightChange, onScroll, getScrollOffset, setScrollOffset]
    );
    const getBottomOffset = useCallback2(
      () => Math.max(
        0,
        contentHeightRef.current - (innerRef.current?.getViewportHeight() || 0)
      ),
      []
    );
    useImperativeHandle2(
      ref,
      () => ({
        scrollTo: (offset) => {
          if (typeof offset !== "number" || isNaN(offset)) {
            return;
          }
          const currentContentHeight = contentHeightRef.current;
          const newScrollTop = Math.max(
            0,
            Math.min(offset, currentContentHeight)
          );
          if (newScrollTop !== getScrollOffset()) {
            setScrollOffset(newScrollTop);
            onScroll?.(newScrollTop);
          }
        },
        scrollBy: (delta) => {
          if (typeof delta !== "number" || isNaN(delta)) {
            return;
          }
          const currentContentHeight = contentHeightRef.current;
          const newScrollTop = Math.max(
            0,
            Math.min(getScrollOffset() + delta, currentContentHeight)
          );
          if (newScrollTop !== getScrollOffset()) {
            setScrollOffset(newScrollTop);
            onScroll?.(newScrollTop);
          }
        },
        scrollToTop: () => {
          if (getScrollOffset() !== 0) {
            setScrollOffset(0);
            onScroll?.(0);
          }
        },
        scrollToBottom: () => {
          const bottomOffset = getBottomOffset();
          if (getScrollOffset() !== bottomOffset) {
            setScrollOffset(bottomOffset);
            onScroll?.(bottomOffset);
          }
        },
        getScrollOffset,
        getContentHeight: () => contentHeightRef.current,
        getViewportHeight: () => innerRef.current?.getViewportHeight() || 0,
        getBottomOffset,
        getItemHeight: (index) => innerRef.current?.getItemHeight(index) || 0,
        getItemPosition: (index) => innerRef.current?.getItemPosition(index) || null,
        remeasure: () => innerRef.current?.remeasure(),
        remeasureItem: (index) => innerRef.current?.remeasureItem(index)
      }),
      [onScroll, getBottomOffset, getScrollOffset, setScrollOffset]
    );
    return /* @__PURE__ */ jsx2(
      ControlledScrollView,
      {
        ref: innerRef,
        scrollOffset,
        onViewportSizeChange,
        onContentHeightChange: handleContentHeightChange,
        onItemHeightChange,
        debug,
        children,
        ...boxProps
      }
    );
  }
);
export {
  ControlledScrollView,
  ScrollView
};
//# sourceMappingURL=index.js.map