import React, { useCallback, useMemo, useState } from 'react';
import classNames from 'classnames';
import Item from './Item';
import { flattenVisible, getDepth } from './utils';
// Resolve childrenStyle.marginLeft to a numeric per-depth indent for the
// absolute layout. Accepts both numbers and "{n}px" strings.
function parseIndentPx(childrenStyle) {
    const ml = childrenStyle && childrenStyle.marginLeft;
    if (typeof ml === 'number' && Number.isFinite(ml)) {
        return ml;
    }
    if (typeof ml === 'string') {
        const n = parseInt(ml, 10);
        return Number.isFinite(n) ? n : 0;
    }
    return 0;
}
const VirtualContainer = React.forwardRef(function VirtualContainer({ items, childrenProperty, childrenStyle, renderAction, isRenderDraggingChildren, theme, itemHeight, viewportHeight, offscreenRowCount = 5, pinnedItemId, }, containerRef) {
    const [scrollTop, setScrollTop] = useState(0);
    // Memoizing the row stream is what makes scrolling cheap. The action-row
    // entries store truthiness only (no rendered React node), so this cache
    // stays valid across drag-state changes — action-row styling depends on
    // isDragging, but action-row *existence* depends only on stable props
    // (actions/addItemLabel/maxDepth). We re-invoke renderAction at render
    // time per visible action row to pick up live styling.
    const rows = useMemo(() => flattenVisible({ items, renderAction, childrenProperty }), [items, renderAction, childrenProperty]);
    const indentPx = parseIndentPx(childrenStyle);
    const totalHeight = rows.length * itemHeight;
    const firstWindowIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - offscreenRowCount);
    const lastWindowIndex = Math.min(rows.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + offscreenRowCount);
    const handleScroll = useCallback(e => {
        setScrollTop(e.currentTarget.scrollTop);
    }, []);
    const containerClass = classNames('nestable-container', 'nestable-top-container', theme && theme.topContainer);
    const visible = [];
    for (let i = firstWindowIndex; i < lastWindowIndex; i++) {
        visible.push({ row: rows[i], absoluteIndex: i });
    }
    // Pin the focused/keyboard-moved row in the slice so its keyboard handlers
    // survive scroll. Without this, arrow-key reorder across viewport edge
    // unmounts the focused Item and onKeyUp dies.
    if (pinnedItemId) {
        const alreadyVisible = visible.some(v => v.row.kind === 'item' && v.row.item.id === pinnedItemId);
        if (!alreadyVisible) {
            const pinnedIdx = rows.findIndex(r => r.kind === 'item' && r.item.id === pinnedItemId);
            if (pinnedIdx !== -1) {
                visible.push({ row: rows[pinnedIdx], absoluteIndex: pinnedIdx });
            }
        }
    }
    return (React.createElement("div", { ref: containerRef, className: containerClass, onScroll: handleScroll, style: {
            height: viewportHeight,
            overflowY: 'auto',
            position: 'relative',
        } },
        React.createElement("div", { style: { height: totalHeight, position: 'relative' } }, visible.map(({ row, absoluteIndex }) => {
            const top = absoluteIndex * itemHeight;
            const left = (row.kind === 'item' ? row.treeDepth - 1 : row.depth - 1) *
                indentPx;
            const positionStyle = {
                position: 'absolute',
                top,
                left,
                right: 0,
                minHeight: itemHeight,
            };
            if (row.kind === 'item') {
                const itemForRender = row.isParentLocked
                    ? { ...row.item, isParentLocked: true, draggable: false }
                    : row.item;
                return (React.createElement("div", { key: `item-${row.item.id}`, style: positionStyle },
                    React.createElement(Item, { id: row.item.id, item: itemForRender, index: row.index, isVeryLastItem: row.isVeryLastItem, siblings: row.siblings, isRenderDraggingChildren: isRenderDraggingChildren, position: row.position, depth: getDepth(row.item, childrenProperty), theme: theme })));
            }
            // Re-invoke renderAction at render time to pick up live styling
            // (depends on state.isDragging in the consumer).
            const actionNode = renderAction({
                siblings: row.siblings,
                item: row.parentItem,
                isRootAction: row.isRootAction,
                veryLastItem: row.veryLastItem,
                depth: row.depth,
            });
            if (!actionNode) {
                return null;
            }
            return (React.createElement("div", { key: `action-${row.parentItem.id}`, style: positionStyle }, actionNode));
        }))));
});
export default VirtualContainer;
//# sourceMappingURL=VirtualContainer.js.map