diff --git a/packages/ui-vue/components/tooltip/src/composition/use-tooltip-position.ts b/packages/ui-vue/components/tooltip/src/composition/use-tooltip-position.ts index 5eec9cd..4eab6b7 100644 --- a/packages/ui-vue/components/tooltip/src/composition/use-tooltip-position.ts +++ b/packages/ui-vue/components/tooltip/src/composition/use-tooltip-position.ts @@ -1,16 +1,63 @@ -import { computed, SetupContext } from "vue"; +import { computed, ref, SetupContext } from "vue"; import { TooltipPlacement, TooltipProps } from "../tooltip.props"; -export function useTooltipPosition(props: TooltipProps, context: SetupContext) { +type RectDirection = 'top' | 'bottom' | 'right' | 'left'; +type RectSizeProperty = 'height' | 'width'; +interface RectPosition { + top: number; + left: number; + right: number; +} + +export function useTooltipPosition( + props: TooltipProps, + context: SetupContext, + hostBound: DOMRect, + tooltipBound: DOMRect, + arrowBound: DOMRect) { const space = 2; const rectifyGutter = 20; + const revertDirectionMap = new Map( + [['top', 'bottom'], ['bottom', 'top'], ['left', 'right'], ['right', 'left']] + ); + + const directionBoundMap = new Map( + [['top', 'height'], ['bottom', 'height'], ['left', 'width'], ['right', 'width']] + ); + + const placement = ref(props.placement); + const tooltipPlacement = computed(() => { return props.placement; }); + function revertPlacement(placement: string, direction: RectDirection) { + const revertDirection: RectDirection = revertDirectionMap.get(direction) || direction; + const revertedPlacement = placement.replace(direction, revertDirection); + return revertDirection; + } + + function autoRectifyDirection( + placement: TooltipPlacement, + referenceBoundingRect: DOMRect, + arrowReferenceBoundingRect: DOMRect, + tooltipBound: DOMRect, + arrowBound: DOMRect) { + let rectifyPlacement = placement; + const direction = placement.split('-')[0] as RectDirection; + const boundProperty = directionBoundMap.get(direction) as RectSizeProperty; + const boundSize = tooltipBound[boundProperty] + arrowBound[boundProperty]; + const referenceBoundSize = Math.abs(arrowReferenceBoundingRect[direction] - referenceBoundingRect[direction]); + + if (referenceBoundSize < boundSize) { + rectifyPlacement = revertPlacement(rectifyPlacement, direction); + } + return rectifyPlacement; + } + function calculateTooltipTopPositoin(placement: TooltipPlacement, hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) { let offsetY = 0; const verticalAlignment = placement.indexOf('bottom') > -1 ? 'bottom' : (placement.indexOf('top') > -1 ? 'top' : 'middle'); @@ -94,9 +141,10 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) { function tryFixOverBound( placement: TooltipPlacement, + originalPosition: RectPosition, referenceBoundingRect: DOMRect, originalBound: DOMRect, - tooltipBound: DOMRect): DOMRect { + tooltipBound: DOMRect): RectPosition { let fixedLeft = originalBound.left; let fixedTop = originalBound.top; if (['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-righ'].includes(placement)) { @@ -111,7 +159,7 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) { fixedTop = overTopBound.overBound ? overTopBound.fixedValue : (overBottomBound.overBound ? overBottomBound.fixedValue - tooltipBound.height : originalBound.top); - return Object.assign({}, originalBound, { left: fixedLeft, top: fixedTop }); + return { left: fixedLeft, top: fixedTop, right: originalPosition.right }; } function calculateArrowPosition( @@ -129,9 +177,23 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) { const fixedArrowLeft = fixedTooltipBound.left - hostBound.left - hostBound.width * 0.5 + arrowBound.width * 0.5 + offsetX; const fixedArrowTop = fixedTooltipBound.top - hostBound.top - hostBound.height * 0.5 + arrowBound.height * 0.5 + offsetY; - return Object.assign({}, arrowBound, { left: fixedArrowLeft, top: fixedArrowTop }); + return Object.assign({}, arrowBound, { left: Math.abs(fixedArrowLeft), top: Math.abs(fixedArrowTop) }); } + const tooltipPosition = computed(() => { + const originalTop = calculateTooltipTopPositoin(placement.value, hostBound, tooltipBound, arrowBound); + const originalLeft = calculateTooltipLeftPosition(placement.value, hostBound, tooltipBound, arrowBound); + const originalRight = calculateTooltipRightPosition(placement.value, hostBound, tooltipBound, arrowBound); + const { top, left, right } = tryFixOverBound( + placement.value, + { top: originalTop, left: originalLeft, right: originalRight }, + hostBound, + tooltipBound, + tooltipBound + ); + return { top, left, right }; + }); + /* 计算tooltip最新位置 */ function calculatePosition(hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) { let top = 0;