1- import type { ComponentInternalInstance , Ref , VNode , VNodeArrayChildren } from 'vue' ;
1+ import type {
2+ ComponentInternalInstance ,
3+ ComponentPublicInstance ,
4+ Ref ,
5+ VNode ,
6+ VNodeArrayChildren ,
7+ } from 'vue' ;
8+ import { unref } from 'vue' ;
29import { cssPrefix } from '../../../mixins/CommonApi' ;
310import { EventListener } from '../../../mixins/DomHelper' ;
411import type {
@@ -14,64 +21,87 @@ const SPACE = 4;
1421/**
1522 * Calculate Tooltip left offset.
1623 *
17- * @param activatorEl Activator Element
18- * @param width Element width
19- * @param placement Tooltip placement.
24+ * @param activatorEl Activator Element
25+ * @param tooltipWidth Tooltip element width
26+ * @param placement Tooltip placement.
2027 * @returns Tooltip left offset
2128 */
2229function getTooltipLeftPosition (
2330 activatorEl : Element ,
24- width : number ,
31+ tooltipWidth : number ,
2532 placement ?: TPlacementPosition
2633) {
27- const offset = activatorEl . getBoundingClientRect ( ) ;
34+ const domRect = activatorEl . getBoundingClientRect ( ) ;
35+ const parentRect = activatorEl . parentElement ?. getBoundingClientRect ( ) ;
2836
2937 switch ( placement ) {
3038 case 'left' :
31- return offset . left - width - SPACE ;
39+ return domRect . left - tooltipWidth - SPACE ;
3240 case 'right' :
33- return offset . left + offset . width + SPACE ;
41+ return domRect . left + domRect . width + SPACE ;
3442 case 'top' :
3543 case 'bottom' :
3644 default :
37- return offset . left + offset . width / 2 - width / 2 ;
45+ return (
46+ domRect . left +
47+ Math . min ( domRect . width / 2 , ( parentRect ?. width ?? domRect . width ) / 2 ) -
48+ tooltipWidth / 2
49+ ) ;
3850 }
3951}
4052
4153/**
4254 * Calculate Tooltip top offset.
4355 *
44- * @param activatorEl Activator Element
45- * @param height Element height
46- * @param placement Tooltip placement.
56+ * @param activatorEl Activator Element
57+ * @param tooltipHeight Tooltip element height
58+ * @param placement Tooltip placement.
4759 * @returns Tooltip top offset
4860 */
4961function getTooltipTopPosition (
5062 activatorEl : Element ,
51- height : number ,
63+ tooltipHeight : number ,
5264 placement ?: TPlacementPosition
5365) {
5466 const rect = activatorEl . getBoundingClientRect ( ) ;
5567
5668 switch ( placement ) {
5769 case 'top' :
58- return rect . top - height - SPACE ;
70+ return rect . top - tooltipHeight - SPACE ;
5971 case 'bottom' :
6072 return rect . top + rect . height + SPACE ;
6173 case 'left' :
6274 case 'right' :
6375 default :
64- return rect . top + rect . height / 2 - height / 2 ;
76+ return rect . top + rect . height / 2 - tooltipHeight / 2 ;
6577 }
6678}
6779
6880/**
6981 * Find first `VNode` within the `BsTooltip` virtual-node subtree.
7082 *
71- * @param instance Component instance search starting point.
83+ * @param instance The tooltip component instance.
84+ * @param triggerEl An element ID or element instance that can trigger the appearance of the tooltip.
7285 * @returns The DOM Element if found.
7386 */
74- function findActivatorElement ( instance : ComponentInternalInstance ) : Element | null {
87+ function findActivatorElement (
88+ instance : ComponentInternalInstance ,
89+ triggerEl ?: string | Element | ComponentPublicInstance
90+ ) : Element | null {
91+ if ( triggerEl ) {
92+ if ( triggerEl instanceof Element ) {
93+ return triggerEl ;
94+ } else if ( Helper . isObject ( triggerEl ) && '$el' in triggerEl ) {
95+ return triggerEl . $el ;
96+ } else if ( Helper . isString ( triggerEl ) ) {
97+ const element = document . getElementById ( triggerEl ) ;
98+ if ( element ) {
99+ return element ;
100+ }
101+ }
102+ }
103+
104+ // Fallback to component instance
75105 const sibling = ( instance . vnode . el as Element ) . nextElementSibling ;
76106 if ( sibling && ! sibling . classList . contains ( `${ cssPrefix } tooltip` ) ) {
77107 // The child-element on "slot.default"
@@ -93,55 +123,63 @@ function findActivatorElement(instance: ComponentInternalInstance): Element | nu
93123}
94124
95125export function useSetTooltipPosition (
126+ activatorRef : Ref < Element | null > ,
96127 tooltipRef : Ref < Element | null > ,
97- instance ?: ComponentInternalInstance | null ,
98- placement ?: TPlacementPosition ,
99- isActive ?: boolean
100- ) {
101- if ( ! tooltipRef . value || ! instance || ! isActive ) {
128+ placement ?: TPlacementPosition
129+ ) : void {
130+ const activatorEl = unref ( activatorRef ) as Element | null ;
131+ const tooltipEl = unref ( tooltipRef ) as HTMLElement | null ;
132+
133+ if ( ! activatorEl || ! tooltipEl ) {
102134 return ;
103135 }
104136
105- const tooltipEl = tooltipRef . value as HTMLElement ;
106-
107137 if ( tooltipEl && Helper . isFunction ( tooltipEl . getBoundingClientRect ) ) {
108- const elRect = tooltipEl . getBoundingClientRect ( ) ;
109- const activatorEl = findActivatorElement ( instance ) ;
110-
111- if ( activatorEl ) {
112- tooltipEl . style . top =
113- getTooltipTopPosition ( activatorEl , elRect . height , placement ) + 'px' ;
114- tooltipEl . style . left =
115- getTooltipLeftPosition ( activatorEl , elRect . width , placement ) + 'px' ;
116- }
138+ const tooltipRect = tooltipEl . getBoundingClientRect ( ) ;
139+
140+ tooltipEl . style . top =
141+ getTooltipTopPosition ( activatorEl , tooltipRect . height , placement ) + 'px' ;
142+ tooltipEl . style . left =
143+ getTooltipLeftPosition ( activatorEl , tooltipRect . width , placement ) + 'px' ;
117144 }
118145}
119146
120147export function useAddTooltipListener (
148+ tooltipRef : Ref < Element | null > ,
149+ activatorRef : Ref < Element | null > ,
150+ active : Ref < boolean > ,
151+ disabled : Ref < boolean > ,
121152 instance : ComponentInternalInstance | null ,
122- active : Ref < boolean >
153+ trigger ?: string | Element | ComponentPublicInstance ,
154+ placement ?: TPlacementPosition
123155) {
124156 if ( ! instance ) {
125157 return ;
126158 }
127159
128- const showTooltip = ( _e : Event ) => {
160+ const showTooltip = ( ) => {
161+ if ( unref ( disabled ) ) {
162+ return ;
163+ }
164+
129165 window . requestAnimationFrame ( ( ) => {
166+ useSetTooltipPosition ( activatorRef , tooltipRef , placement ) ;
130167 instance . emit ( 'update:show' , true ) ;
131168 active . value = true ;
132- } )
133-
169+ } ) ;
134170 // preventEventTarget(e);
135171 } ;
136172 const hideTooltip = ( ) => {
137173 instance . emit ( 'update:show' , false ) ;
138174 active . value = false ;
139175 } ;
140176
141- const activatorEl = findActivatorElement ( instance ) as IHTMLElement | null ;
177+ const activatorEl = findActivatorElement ( instance , trigger ) as IHTMLElement | null ;
178+ activatorRef . value = activatorEl ;
142179
143180 if ( activatorEl ) {
144- const options = { capture : true , passive : false } ;
181+ const options = { capture : true , passive : false } ;
182+
145183 ( activatorEl as IBindingElement ) . __mouseEvents = {
146184 mouseEnter : EventListener . listen ( activatorEl , 'mouseenter' , showTooltip , options ) ,
147185 mouseLeave : EventListener . listen ( activatorEl , 'mouseleave' , hideTooltip , options ) ,
@@ -151,18 +189,16 @@ export function useAddTooltipListener(
151189 }
152190}
153191
154- export function useRemoveTooltipListener ( instance ?: ComponentInternalInstance | null ) {
155- if ( instance ) {
156- const activatorEl = findActivatorElement ( instance ) as IBindingElement | null ;
157-
158- if ( activatorEl ) {
159- // @ts -ignore
160- const { mouseEnter, mouseLeave, focus, blur } = activatorEl . __mouseEvents ;
161- ( mouseEnter as IEventResult ) . remove ( ) ;
162- ( mouseLeave as IEventResult ) . remove ( ) ;
163- ( focus as IEventResult ) . remove ( ) ;
164- ( blur as IEventResult ) . remove ( ) ;
165- activatorEl . __mouseEvents = undefined ;
166- }
192+ export function useRemoveTooltipListener ( activatorRef : Ref < Element | null > ) {
193+ const activatorEl = unref ( activatorRef ) as IBindingElement | null ;
194+
195+ if ( activatorEl ) {
196+ // @ts -ignore
197+ const { mouseEnter, mouseLeave, focus, blur } = activatorEl . __mouseEvents ;
198+ ( mouseEnter as IEventResult ) . remove ( ) ;
199+ ( mouseLeave as IEventResult ) . remove ( ) ;
200+ ( focus as IEventResult ) . remove ( ) ;
201+ ( blur as IEventResult ) . remove ( ) ;
202+ activatorEl . __mouseEvents = undefined ;
167203 }
168204}
0 commit comments