private async Task <ElementPositionInfo> PositionElementRelativeAsync(double gap, Rectangle targetRect, Rectangle boundingRect)
        {
            //previous data... not implemented
            //RTL ... not implemented
            //GetPositionData()
            PositionDirectionalHintData positionData = DirectionalDictionary[DirectionalHint];

            //PositionDirectionalHintData alignmentData = null;
            //GetAlignmentData()
            if (positionData.IsAuto)
            {
                positionData.AlignmentEdge = GetClosestEdge(positionData.TargetEdge, targetRect, boundingRect);
            }

            positionData.AlignTargetEdge = AlignTargetEdge;

            //Now calculate positionedElement
            //GetRectangleFromElement()
            var calloutRectangle = await JSRuntime.InvokeAsync <Rectangle>("BlazorFabricBaseComponent.measureElementRect", calloutReference);

            //Debug.WriteLine($"Callout: {calloutRectangle.left}, {calloutRectangle.top}, {calloutRectangle.right}, {calloutRectangle.bottom}");

            var positionedElement = PositionElementWithinBounds(calloutRectangle, targetRect, boundingRect, positionData, gap);

            var elementPositionInfo = positionedElement.ToElementPositionInfo(targetRect);

            return(elementPositionInfo);
        }
        private ElementPosition FlipToFit(Rectangle rect, Rectangle target, Rectangle bounding, PositionDirectionalHintData positionData, double gap = 0)
        {
            var currentEstimate             = rect;
            var currentEdge                 = positionData.TargetEdge;
            var currentAlignment            = positionData.AlignmentEdge;
            List <RectangleEdge> directions = new List <RectangleEdge> {
                RectangleEdge.Left, RectangleEdge.Right, RectangleEdge.Bottom, RectangleEdge.Top
            };

            for (var i = 0; i < 4; i++)
            {
                if (IsEdgeInBounds(currentEstimate, bounding, currentEdge))
                {
                    directions.RemoveAt(directions.IndexOf(currentEdge));
                    if ((int)directions.IndexOf((RectangleEdge)((int)currentEdge * -1)) > -1)
                    {
                        currentEdge = (RectangleEdge)((int)currentEdge * -1);
                    }
                    else
                    {
                        currentAlignment = currentEdge;
                        currentEdge      = directions[0];
                    }
                    currentEstimate = EstimatePosition(rect, target, new PositionDirectionalHintData(currentEdge, currentAlignment), gap);
                }
                else
                {
                    return(new ElementPosition(currentEstimate, currentEdge, currentAlignment));
                }
            }
            return(new ElementPosition(rect, positionData.TargetEdge, currentAlignment));
        }
        private Rectangle EstimatePosition(Rectangle elementToPosition, Rectangle target, PositionDirectionalHintData positionData, double gap = 0)
        {
            var       elementEdge = this.CoverTarget ? positionData.TargetEdge : (RectangleEdge)((int)positionData.TargetEdge * -1);
            Rectangle estimatedElementPosition = null;

            estimatedElementPosition = this.CoverTarget
                ? AlignEdges(elementToPosition, target, positionData.TargetEdge, gap)
                : AlignOppositeEdges(elementToPosition, target, positionData.TargetEdge, gap);
            if (positionData.AlignmentEdge == RectangleEdge.None)
            {
                var targetMiddlePoint = GetCenterValue(target, positionData.TargetEdge);
                estimatedElementPosition = CenterEdgeToPoint(estimatedElementPosition, elementEdge, targetMiddlePoint);
            }
            else
            {
                estimatedElementPosition = AlignEdges(estimatedElementPosition, target, positionData.AlignmentEdge);
            }
            return(estimatedElementPosition);
        }
        private ElementPosition AdjustFitWithinBounds(Rectangle element, Rectangle target, Rectangle bounding, PositionDirectionalHintData positionData, double gap = 0)
        {
            var             alignmentEdge   = positionData.AlignmentEdge;
            var             alignTargetEdge = positionData.AlignTargetEdge;
            ElementPosition elementEstimate = new ElementPosition(element, positionData.TargetEdge, alignmentEdge);

            if (!DirectionalHintFixed && !CoverTarget)
            {
                elementEstimate = FlipToFit(element, target, bounding, positionData, gap);
            }
            var outOfBounds = GetOutOfBoundsEdges(element, bounding);

            if (alignTargetEdge)
            {
                // The edge opposite to the alignment edge might be out of bounds. Flip alignment to see if we can get it within bounds.
                if (elementEstimate.AlignmentEdge != RectangleEdge.None && outOfBounds.IndexOf((RectangleEdge)((int)elementEstimate.AlignmentEdge * -1)) > -1)
                {
                    var flippedElementEstimate = FlipAlignmentEdge(elementEstimate, target, gap);
                    if (IsRectangleWithinBounds(flippedElementEstimate.ElementRectangle, bounding))
                    {
                        return(flippedElementEstimate);
                    }
                }
            }
            else
            {
                foreach (var direction in outOfBounds)
                {
                    elementEstimate.ElementRectangle = AlignEdges(elementEstimate.ElementRectangle, bounding, direction);
                }
            }
            return(elementEstimate);
        }
        private ElementPosition PositionElementWithinBounds(Rectangle elementToPosition, Rectangle target, Rectangle bounding, PositionDirectionalHintData positionData, double gap)
        {
            var estimatedElementPosition = EstimatePosition(elementToPosition, target, positionData, gap);

            if (IsRectangleWithinBounds(estimatedElementPosition, bounding))
            {
                return(new ElementPosition(estimatedElementPosition, positionData.TargetEdge, positionData.AlignmentEdge));
            }
            else
            {
                return(AdjustFitWithinBounds(elementToPosition, target, bounding, positionData, gap));
            }
        }