/**
 * @returns The DOMBox for the area where the target element has to fit into.
 */
function fitInfo(option = {}) {
    const { fitInto = window } = option;
    let result;
    if (fitInto === window) {
        result = new DOMRect(0, 0, fitInto.innerWidth, fitInto.innerHeight);
    }
    else {
        result = fitInto.getBoundingClientRect();
    }
    return result;
}
/**
 * @param target The target overlay element.
 * @returns true when the target overlay element hast the RTL directive (right-to-left)
 */
function isRTL(target) {
    // elements inherit this property so no need to check anything else.
    return window.getComputedStyle(target).direction === 'rtl';
}
/**
 * Computes horizontal align for the current locale.
 *
 * @param target The target overlay element
 * @param align The current horizontal align
 * @returns Locale specific horizontal align
 */
function localeHorizontal(target, align) {
    if (isRTL(target)) {
        if (align === 'right') {
            return 'left';
        }
        if (align === 'left') {
            return 'right';
        }
    }
    return align;
}
/**
 * @param anchorRect The anchor element DOMRect object
 * @param options Computation options
 * @returns The vertical position of the target for the top position
 */
function verticalTop(anchorRect, noOverlap) {
    // from the anchor's top line down.
    let { top } = anchorRect;
    if (noOverlap) {
        top += anchorRect.height;
    }
    return top;
}
/**
 * @param anchorRect The anchor element's DOMRect object
 * @param targetHeight The computed height of the target element
 * @param options Computation options
 * @returns The vertical position of the target for the bottom position
 */
function verticalBottom(anchorRect, targetHeight, noOverlap) {
    // from the anchor's bottom line up.
    let top = anchorRect.bottom - targetHeight;
    if (noOverlap) {
        top -= anchorRect.height;
    }
    return top;
}
/**
 * @param anchorRect The anchor element's DOMRect object
 * @param targetHeight The computed height of the target element
 * @returns The vertical position of the target for the middle position
 */
function verticalMiddle(anchorRect, targetHeight) {
    return anchorRect.top + anchorRect.height / 2 - targetHeight / 2;
}
function verticalAuto(anchorRect, fitRect, targetHeight, noOverlap) {
    // we position the element top or bottom depending where we can fit it
    // in the position target.
    // try top position first
    let top = verticalTop(anchorRect, noOverlap);
    if (top + targetHeight > fitRect.bottom) {
        // try bottom
        top = verticalBottom(anchorRect, targetHeight, noOverlap);
        if (top < fitRect.top) {
            // We can't fit the target into either direction so we
            // position it in the middle and constrain the target to the size of the viewport.
            top = verticalMiddle(anchorRect, targetHeight);
        }
    }
    return top;
}
function horizontalLeft(anchorRect, noOverlap) {
    let { left } = anchorRect;
    if (noOverlap) {
        left += anchorRect.width;
    }
    return left;
}
function horizontalRight(anchorRect, targetWidth, noOverlap) {
    let left = anchorRect.right - targetWidth;
    if (noOverlap) {
        left -= anchorRect.width;
    }
    return left;
}
function horizontalMiddle(anchorRect, targetWidth) {
    return anchorRect.left + anchorRect.width / 2 - targetWidth / 2;
}
function horizontalAuto(anchorRect, fitRect, targetWidth, noOverlap) {
    // we position the element left or right depending where we can fit it
    // in the position target.
    // try left position first
    let left = horizontalLeft(anchorRect, noOverlap);
    if (left + targetWidth > fitRect.right) {
        // try right position
        left = horizontalRight(anchorRect, targetWidth, noOverlap);
        if (left < fitRect.left) {
            // We can't fit the target into either direction so we
            // position it in the middle and constrain the target to the size of the viewport.
            left = horizontalMiddle(anchorRect, targetWidth);
        }
    }
    return left;
}
/**
 * Positions an overlay relative to the anchor element.
 *
 * @param target The target element that has to be positioned relative to the anchor element
 * @param anchor The anchor element. It is used to anchor the position of the overlay.
 * @param options Positioning options.
 * @returns The list of styles to be used to position the target. Can be used with the `styleMap` directive.
 */
export function positionOverlay(target, anchor, options = {}) {
    const result = {};
    const anchorRect = anchor.getBoundingClientRect();
    const fitRect = fitInfo(options);
    const targetWidth = target.offsetWidth; // with margins and everything
    const targetHeight = target.offsetHeight;
    let top;
    let left;
    if (options.vertical === 'top') {
        // from the anchor's top line down.
        top = verticalTop(anchorRect, options.noOverlap);
    }
    else if (options.vertical === 'bottom') {
        // from the anchor's bottom line up.
        top = verticalBottom(anchorRect, targetHeight, options.noOverlap);
    }
    else if (options.vertical === 'middle') {
        top = verticalMiddle(anchorRect, targetHeight);
    }
    else if (options.vertical === 'auto') {
        top = verticalAuto(anchorRect, fitRect, targetHeight, options.noOverlap);
    }
    else {
        // this mimics the natural box layout.
        top = verticalTop(anchorRect, true);
    }
    const locHorizontal = localeHorizontal(target, options.horizontal);
    if (locHorizontal === 'left') {
        left = horizontalLeft(anchorRect, options.noOverlap);
    }
    else if (locHorizontal === 'right') {
        left = horizontalRight(anchorRect, targetWidth, options.noOverlap);
    }
    else if (locHorizontal === 'middle') {
        left = horizontalMiddle(anchorRect, targetWidth);
    }
    else if (locHorizontal === 'auto') {
        left = horizontalAuto(anchorRect, fitRect, targetWidth, options.noOverlap);
    }
    else {
        left = horizontalLeft(anchorRect, false);
    }
    if (top !== undefined) {
        if (options.verticalOffset) {
            top += options.verticalOffset;
        }
        result.top = `${top}px`;
    }
    if (left !== undefined) {
        if (options.horizontalOffset) {
            left += options.horizontalOffset;
        }
        result.left = `${left}px`;
    }
    if (options.matchAnchorWidth) {
        result.width = `${anchorRect.width}px`;
    }
    if (options.constrain) {
        result.overflow = 'auto';
        const { constrainPaddingY = 0 } = options;
        const { height } = fitRect;
        if (options.vertical === 'top') {
            // from target's `top` to the bottom of `fit`.
            const max = height - top - constrainPaddingY;
            if (max > 0) {
                result.maxHeight = `${max}px`;
            }
        }
    }
    return result;
}
//# sourceMappingURL=ElementPositioning.js.map