feature(tooltip): add use-tooltip-position

This commit is contained in:
Sagi 2022-10-12 19:26:11 +08:00
parent 3a2e018967
commit 07bdfe1c38
3 changed files with 202 additions and 7 deletions

View File

@ -0,0 +1,190 @@
import { computed, SetupContext } from "vue";
import { TooltipPlacement, TooltipProps } from "../tooltip.props";
export function useTooltipPosition(props: TooltipProps, context: SetupContext) {
const space = 2;
const tooltipPlacement = computed(() => {
return props.placement;
});
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');
const originalPositionY = verticalAlignment === 'bottom' ? hostBound.bottom : hostBound.top;
if (['top', 'top-left', 'top-right'].includes(placement)) {
offsetY = 0 - tooltipBound.height - space;
} else if (['left', 'right'].includes(placement)) {
offsetY = (hostBound.height - tooltipBound.height) / 2;
} else if (['bottom', 'bottom-left', 'bottom-right'].includes(placement)) {
offsetY = arrowBound.height + 2;
} else if (['right-bottom', 'left-bottom'].includes(placement)) {
offsetY = 0 - tooltipBound.height;
} else if (['left-top', 'right-top'].includes(placement)) {
offsetY = 0;
} else {
return 0;
}
return originalPositionY + offsetY;
}
function calculateTooltipLeftPosition(placement: TooltipPlacement, hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) {
let offsetX = 0;
const horizontalAlignment = placement.indexOf('right') > -1 ? 'right' : (placement.indexOf('left') > -1 ? 'left' : 'center');
const originalPositionX = horizontalAlignment === 'right' ? hostBound.right : hostBound.left;
if (['left', 'left-top', 'left-bottom'].includes(placement)) {
offsetX = 0 - tooltipBound.width - space;
} else if (['top', 'bottom'].includes(placement)) {
offsetX = (hostBound.width - tooltipBound.width) / 2;
} else if (['right', 'right-top', 'right-bottom'].includes(placement)) {
offsetX = space;
} else if (['top-right', 'bottom-right'].includes(placement)) {
offsetX = 0 - tooltipBound.width;
} else if (['top-left', 'bottom-left'].includes(placement)) {
offsetX = 0;
} else {
return 0;
}
return originalPositionX + offsetX;
}
function calculateTooltipRightPosition(placement: TooltipPlacement, hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) {
let offsetX = 0;
const horizontalAlignment = placement.indexOf('right') > -1 ? 'right' : (placement.indexOf('left') > -1 ? 'left' : 'center');
const originalPositionX = horizontalAlignment === 'right' ? hostBound.right : hostBound.left;
if (['top', 'bottom'].includes(placement)) {
offsetX = (hostBound.width - tooltipBound.width) / 2 + tooltipBound.width;
} else if (['top-left', 'bottom-left'].includes(placement)) {
offsetX = tooltipBound.width;
} else if (['top-right', 'bottom-right'].includes(placement)) {
offsetX = 0;
} else {
return 0;
}
return originalPositionX + offsetX;
}
/* 计算tooltip最新位置 */
function calculatePosition(hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) {
let top = 0;
let left = 0;
let right = 0;
switch (this.rectifyPlacement) {
case 'top':
top = hostPosition.top - tooltipSize.height - 2;
left = hostPosition.left + (hostPosition.width - tooltipSize.width) / 2;
right = left + tooltipSize.width;
break;
case 'left':
top = hostPosition.top + (hostPosition.height - tooltipSize.height) / 2;
left = hostPosition.left - tooltipSize.width - 2;
break;
case 'right':
top = hostPosition.top + (hostPosition.height - tooltipSize.height) / 2;
left = hostPosition.right + 2;
break;
case 'bottom':
top = hostPosition.bottom + arrowSize.height + 2;
left = hostPosition.left + (hostPosition.width - tooltipSize.width) / 2;
right = left + tooltipSize.width;
break;
case 'top-left':
top = hostPosition.top - tooltipSize.height - 2;
left = hostPosition.left;
right = left + tooltipSize.width;
break;
case 'top-right':
top = hostPosition.top - tooltipSize.height - 2;
left = hostPosition.right - tooltipSize.width;
right = hostPosition.right;
break;
case 'right-top':
top = hostPosition.top;
left = hostPosition.right + 2;
this.arrowNode.nativeElement.style.top = '10%';
break;
case 'right-bottom':
top = hostPosition.bottom - tooltipSize.height;
left = hostPosition.right + 2;
this.arrowNode.nativeElement.style.bottom = '10%';
break;
case 'bottom-left':
top = hostPosition.bottom + arrowSize.height + 2;
left = hostPosition.left;
right = left + tooltipSize.width;
break;
case 'bottom-right':
top = hostPosition.bottom + arrowSize.height + 2;
left = hostPosition.right - tooltipSize.width;
right = hostPosition.right;
break;
case 'left-top':
top = hostPosition.top;
left = hostPosition.left - tooltipSize.width - 2;
this.arrowNode.nativeElement.style.top = '10%';
break;
case 'left-bottom':
top = hostPosition.bottom - tooltipSize.height;
left = hostPosition.left - tooltipSize.width - 2;
this.arrowNode.nativeElement.style.bottom = '10%';
}
let overResult;
let arrowLeft = 0;
let arrowTop = 0
switch (this.rectifyPlacement) {
case 'top':
case 'top-left':
case 'top-right':
case 'bottom':
case 'bottom-left':
case 'bottom-right':
overResult = this.isOverBounding('left', left);
if (overResult.isOver) {
left = overResult.newValue;
} else {
overResult = this.isOverBounding('right', right);
if (overResult.isOver) {
left = overResult.newValue - tooltipSize.width;
}
}
arrowLeft = left - hostPosition.left - hostPosition.width * 0.5 + arrowSize.width * 0.5;
if (this.rectifyPlacement.indexOf('-left') > 0) {
arrowLeft += hostPosition.width * 0.4;
} else if (this.rectifyPlacement.indexOf('-right') > 0) {
arrowLeft -= hostPosition.width * 0.4;
}
this.arrowNode.nativeElement.style.left = Math.abs(arrowLeft) + 'px';
break;
default:
overResult = this.isOverBounding('top', top);
if (overResult.isOver) {
top = overResult.newValue;
} else {
overResult = this.isOverBounding('bottom', bottom);
if (overResult.isOver) {
top = overResult.newValue - tooltipSize.height;
}
}
arrowTop = top - hostPosition.top - hostPosition.height * 0.5 + arrowSize.height * 0.5;;
if (this.rectifyPlacement.indexOf('-top') > 0) {
arrowTop += hostPosition.height * 0.4;
} else if (this.rectifyPlacement.indexOf('-bottom') > 0) {
arrowTop -= hostPosition.height * 0.4;
}
this.arrowNode.nativeElement.style.top = Math.abs(arrowTop) + 'px';
}
this.tooltipNode.nativeElement.style.top = top + 'px';
this.tooltipNode.nativeElement.style.left = left + 'px';
}
}

View File

@ -10,10 +10,15 @@ export default defineComponent({
setup(props: TooltipProps, context: SetupContext) {
const isTextContext = ref(true);
const tooltipClass = computed(() => ({
tooltip: true,
show: true
}));
const tooltipClass = computed(() => {
const classObject = {
tooltip: true,
show: true
} as any;
const tooltipClassName = `bs-tooltip-${props.position}`;
classObject[tooltipClassName] = true;
return classObject;
});
const shouldShowTooltipText = computed(() => isTextContext.value);
@ -21,7 +26,7 @@ export default defineComponent({
return () => {
return (
<div class={tooltipClass}>
<div class={tooltipClass.value}>
<div class="arrow"></div>
<div class="tooltip-inner">
<div class="tooltip-tmpl">

View File

@ -1,6 +1,6 @@
import { ExtractPropTypes, PropType } from 'vue';
export type TooltipPosition =
export type TooltipPlacement =
| 'top'
| 'top-left'
| 'top-right'
@ -18,7 +18,7 @@ export const tooltipProps = {
content: { type: String },
width: { type: Number },
customClass: { type: String },
position: { type: String as PropType<TooltipPosition>, default: 'top' },
placement: { type: String as PropType<TooltipPlacement>, default: 'top' },
referenceBoundingRect: { type: Object, default: {} }
};
export type TooltipProps = ExtractPropTypes<typeof tooltipProps>;