@qin-ui/antd-vue-pro 2.6.1 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api.json +2 -2
- package/es/antd-vue-pro.css +6 -8
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
- package/es/components/table/components/TableEllipsisTooltip.vue.d.ts +2 -0
- package/es/components/table/components/TableEllipsisTooltip.vue.d.ts.map +1 -1
- package/es/components/table/hooks/useEllipsisTooltip.d.ts +9 -3
- package/es/components/table/hooks/useEllipsisTooltip.d.ts.map +1 -1
- package/es/index.js +247 -95
- package/es/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/components/ProForm/index.vue +3 -3
- package/src/components/table/components/ProTable.vue +9 -5
- package/src/components/table/components/TableEllipsisTooltip.vue +65 -8
- package/src/components/table/hooks/useEllipsisTooltip.ts +177 -28
package/package.json
CHANGED
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
* @param {...FormProps} ...attrs - Ant Design Vue Form 组件的其他属性
|
|
31
31
|
*
|
|
32
32
|
* @slot default - 默认插槽,可用于添加额外内容
|
|
33
|
-
* @slot [path] - 以字段 path
|
|
33
|
+
* @slot [path] - 以字段 path 命名的动态插槽,优先于 component 渲染;作用域含字段值 value、回写通道 onUpdate:value、disabled、path 与合并后的控件属性,可直接 v-bind 给控件
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
36
|
* ```vue
|
|
37
37
|
* <template>
|
|
38
38
|
* <ProForm :form="form" @submit="handleSubmit">
|
|
39
|
-
* <template #name="
|
|
40
|
-
* <a-input
|
|
39
|
+
* <template #name="scoped">
|
|
40
|
+
* <a-input v-bind="scoped" />
|
|
41
41
|
* </template>
|
|
42
42
|
* </ProForm>
|
|
43
43
|
* </template>
|
|
@@ -489,6 +489,11 @@ const ellipsisTooltipProps = computed(() => {
|
|
|
489
489
|
return rest;
|
|
490
490
|
});
|
|
491
491
|
|
|
492
|
+
const ellipsisTooltipOverlay = shallowRef<HTMLElement | null>(null);
|
|
493
|
+
const setEllipsisTooltipOverlay = (element: HTMLElement | null) => {
|
|
494
|
+
ellipsisTooltipOverlay.value = element;
|
|
495
|
+
};
|
|
496
|
+
|
|
492
497
|
const {
|
|
493
498
|
open: ellipsisTooltipOpen,
|
|
494
499
|
content: ellipsisTooltipContent,
|
|
@@ -500,7 +505,8 @@ const {
|
|
|
500
505
|
rootRef,
|
|
501
506
|
ellipsisTooltipEnabled,
|
|
502
507
|
ellipsisTooltipDelay,
|
|
503
|
-
ellipsisTooltipLeaveDelay
|
|
508
|
+
ellipsisTooltipLeaveDelay,
|
|
509
|
+
ellipsisTooltipOverlay
|
|
504
510
|
);
|
|
505
511
|
|
|
506
512
|
// 数据源优先级:显式绑定 > table 实例(useTable 创建的 dataSource)
|
|
@@ -746,6 +752,7 @@ defineExpose({
|
|
|
746
752
|
:anchor-style="ellipsisTooltipAnchorStyle"
|
|
747
753
|
:container="ellipsisTooltipContainer"
|
|
748
754
|
v-bind="ellipsisTooltipProps"
|
|
755
|
+
:set-overlay-element="setEllipsisTooltipOverlay"
|
|
749
756
|
/>
|
|
750
757
|
</div>
|
|
751
758
|
</template>
|
|
@@ -756,15 +763,12 @@ defineExpose({
|
|
|
756
763
|
display: flex;
|
|
757
764
|
align-items: center;
|
|
758
765
|
justify-content: flex-end;
|
|
766
|
+
margin-bottom: 8px;
|
|
759
767
|
|
|
760
768
|
&:empty {
|
|
761
769
|
display: none;
|
|
762
770
|
}
|
|
763
771
|
|
|
764
|
-
& + &_content {
|
|
765
|
-
margin-top: 16px;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
772
|
&_button-bar {
|
|
769
773
|
flex: 1;
|
|
770
774
|
}
|
|
@@ -6,9 +6,20 @@
|
|
|
6
6
|
* @description 以固定定位锚点承载单个宿主 Tooltip,锚点位置由 ProTable 按悬停单元格更新;
|
|
7
7
|
* 整个表格只渲染一个 Tooltip 实例,实例数量不随单元格数量增长。
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
nextTick,
|
|
11
|
+
onBeforeUnmount,
|
|
12
|
+
ref,
|
|
13
|
+
useAttrs,
|
|
14
|
+
watch,
|
|
15
|
+
type Component,
|
|
16
|
+
} from 'vue';
|
|
10
17
|
import { Tooltip as ATooltip } from '../../../shared/ui';
|
|
11
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
registerEllipsisTooltipOverlay,
|
|
20
|
+
unregisterEllipsisTooltipOverlay,
|
|
21
|
+
type EllipsisTooltipAnchorStyle,
|
|
22
|
+
} from '../hooks/useEllipsisTooltip';
|
|
12
23
|
import type { EllipsisTooltipConfig } from '../types';
|
|
13
24
|
|
|
14
25
|
defineOptions({ name: 'TableEllipsisTooltip', inheritAttrs: false });
|
|
@@ -23,6 +34,7 @@ type Props = {
|
|
|
23
34
|
content?: string;
|
|
24
35
|
anchorStyle?: EllipsisTooltipAnchorStyle;
|
|
25
36
|
container?: () => HTMLElement | null | undefined;
|
|
37
|
+
setOverlayElement?: (element: HTMLElement | null) => void;
|
|
26
38
|
} & /* @vue-ignore */ HostTooltipProps;
|
|
27
39
|
|
|
28
40
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -34,6 +46,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
34
46
|
opacity: 0,
|
|
35
47
|
}),
|
|
36
48
|
container: () => undefined,
|
|
49
|
+
setOverlayElement: () => undefined,
|
|
37
50
|
});
|
|
38
51
|
const attrs = useAttrs() as HostTooltipProps;
|
|
39
52
|
const HostTooltip = ATooltip as unknown as Component;
|
|
@@ -41,14 +54,58 @@ const HostTooltip = ATooltip as unknown as Component;
|
|
|
41
54
|
type TooltipInstance = {
|
|
42
55
|
forceAlign?: () => void;
|
|
43
56
|
forcePopupAlign?: () => void;
|
|
57
|
+
getPopupDomNode?: () => HTMLElement | undefined;
|
|
44
58
|
};
|
|
45
59
|
|
|
46
60
|
const tooltipRef = ref<TooltipInstance | null>(null);
|
|
61
|
+
let currentOverlay: HTMLElement | null = null;
|
|
62
|
+
let overlaySyncToken = 0;
|
|
63
|
+
|
|
64
|
+
const getContainerNode = (): HTMLElement | undefined => {
|
|
65
|
+
const container = props.container?.();
|
|
66
|
+
if (container) return container;
|
|
67
|
+
return typeof document !== 'undefined' ? document.body : undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** 读取宿主 Tooltip 浮层节点 */
|
|
71
|
+
const getPopupElement = (): HTMLElement | null =>
|
|
72
|
+
tooltipRef.value?.getPopupDomNode?.() ?? null;
|
|
73
|
+
|
|
74
|
+
/** 同步浮层节点引用与全局登记 */
|
|
75
|
+
const syncOverlayElement = (next: HTMLElement | null) => {
|
|
76
|
+
if (currentOverlay === next) return;
|
|
77
|
+
if (currentOverlay) unregisterEllipsisTooltipOverlay(currentOverlay);
|
|
78
|
+
currentOverlay = next;
|
|
79
|
+
props.setOverlayElement(next);
|
|
80
|
+
if (next) registerEllipsisTooltipOverlay(next);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** 浮层渲染后同步节点引用,宿主延迟挂载时重试 */
|
|
84
|
+
const syncOverlayFromDom = async () => {
|
|
85
|
+
if (!props.open) return;
|
|
86
|
+
const token = ++overlaySyncToken;
|
|
87
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
88
|
+
const popup = getPopupElement();
|
|
89
|
+
if (popup) {
|
|
90
|
+
if (token === overlaySyncToken) syncOverlayElement(popup);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!props.open) return;
|
|
94
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
95
|
+
}
|
|
96
|
+
if (token === overlaySyncToken) syncOverlayElement(null);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const clearOverlay = () => {
|
|
100
|
+
overlaySyncToken += 1;
|
|
101
|
+
syncOverlayElement(null);
|
|
102
|
+
};
|
|
47
103
|
|
|
48
104
|
/** 锚点或内容变化后重算宿主浮层位置 */
|
|
49
105
|
const forceAlign = async () => {
|
|
50
106
|
if (!props.open) return;
|
|
51
107
|
await nextTick();
|
|
108
|
+
syncOverlayFromDom();
|
|
52
109
|
const instance = tooltipRef.value;
|
|
53
110
|
(instance?.forceAlign ?? instance?.forcePopupAlign)?.();
|
|
54
111
|
};
|
|
@@ -57,15 +114,15 @@ watch(() => props.anchorStyle, forceAlign, { deep: true });
|
|
|
57
114
|
watch(
|
|
58
115
|
() => props.open,
|
|
59
116
|
value => {
|
|
60
|
-
if (value)
|
|
117
|
+
if (value) {
|
|
118
|
+
forceAlign();
|
|
119
|
+
} else {
|
|
120
|
+
clearOverlay();
|
|
121
|
+
}
|
|
61
122
|
}
|
|
62
123
|
);
|
|
63
124
|
|
|
64
|
-
|
|
65
|
-
const container = props.container?.();
|
|
66
|
-
if (container) return container;
|
|
67
|
-
return typeof document !== 'undefined' ? document.body : undefined;
|
|
68
|
-
};
|
|
125
|
+
onBeforeUnmount(clearOverlay);
|
|
69
126
|
|
|
70
127
|
const getPopupContainer = () => getContainerNode() ?? document.body;
|
|
71
128
|
</script>
|
|
@@ -13,6 +13,19 @@ export const ELLIPSIS_TOOLTIP_CELL_ATTR = 'data-pro-table-ellipsis';
|
|
|
13
13
|
/** ProTable 根节点归属标记 */
|
|
14
14
|
export const PRO_TABLE_ROOT_ATTR = 'data-pro-table-root';
|
|
15
15
|
|
|
16
|
+
/** 当前打开中的省略 Tooltip 浮层 */
|
|
17
|
+
const activeOverlays = new Set<HTMLElement>();
|
|
18
|
+
|
|
19
|
+
/** 登记省略 Tooltip 浮层 */
|
|
20
|
+
export const registerEllipsisTooltipOverlay = (overlay: HTMLElement) => {
|
|
21
|
+
activeOverlays.add(overlay);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** 注销省略 Tooltip 浮层 */
|
|
25
|
+
export const unregisterEllipsisTooltipOverlay = (overlay: HTMLElement) => {
|
|
26
|
+
activeOverlays.delete(overlay);
|
|
27
|
+
};
|
|
28
|
+
|
|
16
29
|
/** 共享 Tooltip 锚点样式 */
|
|
17
30
|
export type EllipsisTooltipAnchorStyle = {
|
|
18
31
|
position: 'fixed';
|
|
@@ -68,24 +81,85 @@ const measureCell = (cell: HTMLElement): CellMeasurement => {
|
|
|
68
81
|
};
|
|
69
82
|
};
|
|
70
83
|
|
|
84
|
+
/** 单元格内容盒矩形 */
|
|
85
|
+
type ContentRect = {
|
|
86
|
+
left: number;
|
|
87
|
+
top: number;
|
|
88
|
+
width: number;
|
|
89
|
+
height: number;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** 解析计算样式中的像素长度,非法值按 0 处理 */
|
|
93
|
+
const toPixel = (value: string) => Number.parseFloat(value) || 0;
|
|
94
|
+
|
|
95
|
+
/** 读取单元格内容盒矩形:扣除边框与内边距 */
|
|
96
|
+
const measureContentRect = (cell: HTMLElement): ContentRect => {
|
|
97
|
+
const rect = cell.getBoundingClientRect();
|
|
98
|
+
const style = getComputedStyle(cell);
|
|
99
|
+
const insetLeft = toPixel(style.borderLeftWidth) + toPixel(style.paddingLeft);
|
|
100
|
+
const insetTop = toPixel(style.borderTopWidth) + toPixel(style.paddingTop);
|
|
101
|
+
const insetRight =
|
|
102
|
+
toPixel(style.borderRightWidth) + toPixel(style.paddingRight);
|
|
103
|
+
const insetBottom =
|
|
104
|
+
toPixel(style.borderBottomWidth) + toPixel(style.paddingBottom);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
left: rect.left + insetLeft,
|
|
108
|
+
top: rect.top + insetTop,
|
|
109
|
+
width: Math.max(0, rect.width - insetLeft - insetRight),
|
|
110
|
+
height: Math.max(0, rect.height - insetTop - insetBottom),
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** 读取单元格文本行矩形并裁剪到内容盒,文本行不可用时回退内容盒 */
|
|
115
|
+
const measureTextRect = (
|
|
116
|
+
cell: HTMLElement,
|
|
117
|
+
content: ContentRect
|
|
118
|
+
): ContentRect => {
|
|
119
|
+
if (
|
|
120
|
+
typeof document === 'undefined' ||
|
|
121
|
+
typeof document.createRange !== 'function'
|
|
122
|
+
) {
|
|
123
|
+
return content;
|
|
124
|
+
}
|
|
125
|
+
const range = document.createRange();
|
|
126
|
+
range.selectNodeContents(cell);
|
|
127
|
+
const rect = range.getBoundingClientRect();
|
|
128
|
+
if (!rect.width || !rect.height) return content;
|
|
129
|
+
const left = Math.max(rect.left, content.left);
|
|
130
|
+
const top = Math.max(rect.top, content.top);
|
|
131
|
+
const right = Math.min(rect.right, content.left + content.width);
|
|
132
|
+
const bottom = Math.min(rect.bottom, content.top + content.height);
|
|
133
|
+
if (right <= left || bottom <= top) return content;
|
|
134
|
+
return {
|
|
135
|
+
left,
|
|
136
|
+
top,
|
|
137
|
+
width: right - left,
|
|
138
|
+
height: bottom - top,
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
71
142
|
/**
|
|
72
143
|
* 表格级共享省略 Tooltip
|
|
73
144
|
*
|
|
74
145
|
* @description
|
|
75
|
-
* 在 ProTable 根节点上以事件委托识别带标记的 body 单元格,维护单个 Tooltip
|
|
76
|
-
*
|
|
146
|
+
* 在 ProTable 根节点上以事件委托识别带标记的 body 单元格,维护单个 Tooltip 的显隐、文本与按单元格文本行矩形计算的固定定位锚点,文本行矩形不可用时回退单元格内容盒;
|
|
147
|
+
* 仅在单元格文本横向溢出时触发;鼠标移入浮层时保持展示,移出浮层后按离开延迟关闭;
|
|
148
|
+
* 表格滚动、窗口尺寸变化、KeepAlive 停用时关闭,卸载时移除监听。
|
|
77
149
|
*
|
|
78
150
|
* @param rootRef - ProTable 根节点引用
|
|
79
151
|
* @param enabled - 是否启用共享 Tooltip;为 false 时不注册全局监听
|
|
80
152
|
* @param delay - 悬停延迟(ms),0 表示立即展示
|
|
81
153
|
* @param leaveDelay - 移出延迟(ms),0 表示立即关闭
|
|
154
|
+
* @param overlayElement - 当前 Tooltip 浮层节点引用
|
|
82
155
|
* @returns Tooltip 状态与根节点事件处理器
|
|
83
156
|
*/
|
|
84
157
|
export function useEllipsisTooltip(
|
|
85
158
|
rootRef: Ref<HTMLElement | null | undefined>,
|
|
86
159
|
enabled: Ref<boolean>,
|
|
87
160
|
delay: Ref<number>,
|
|
88
|
-
leaveDelay: Ref<number
|
|
161
|
+
leaveDelay: Ref<number>,
|
|
162
|
+
overlayElement: Ref<HTMLElement | null | undefined>
|
|
89
163
|
): EllipsisTooltipState {
|
|
90
164
|
const open = ref(false);
|
|
91
165
|
const content = ref('');
|
|
@@ -94,14 +168,18 @@ export function useEllipsisTooltip(
|
|
|
94
168
|
});
|
|
95
169
|
let activeCell: HTMLElement | null = null;
|
|
96
170
|
let pendingCell: HTMLElement | null = null;
|
|
171
|
+
let pendingContent = '';
|
|
172
|
+
let pendingRect: ContentRect | null = null;
|
|
97
173
|
let openTimer: ReturnType<typeof setTimeout> | null = null;
|
|
98
174
|
let closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
99
175
|
|
|
100
176
|
const clearOpenTimer = () => {
|
|
177
|
+
pendingCell = null;
|
|
178
|
+
pendingContent = '';
|
|
179
|
+
pendingRect = null;
|
|
101
180
|
if (openTimer === null) return;
|
|
102
181
|
clearTimeout(openTimer);
|
|
103
182
|
openTimer = null;
|
|
104
|
-
pendingCell = null;
|
|
105
183
|
};
|
|
106
184
|
|
|
107
185
|
const clearCloseTimer = () => {
|
|
@@ -110,16 +188,82 @@ export function useEllipsisTooltip(
|
|
|
110
188
|
closeTimer = null;
|
|
111
189
|
};
|
|
112
190
|
|
|
191
|
+
const getEventRoot = (node: Node | null) => {
|
|
192
|
+
const element = node instanceof Element ? node : node?.parentElement;
|
|
193
|
+
return element?.closest?.(`[${PRO_TABLE_ROOT_ATTR}]`) ?? null;
|
|
194
|
+
};
|
|
195
|
+
|
|
113
196
|
const close = () => {
|
|
114
197
|
clearOpenTimer();
|
|
115
198
|
clearCloseTimer();
|
|
116
199
|
open.value = false;
|
|
117
200
|
activeCell = null;
|
|
201
|
+
removeOverlayListeners();
|
|
118
202
|
};
|
|
119
203
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
204
|
+
const scheduleClose = () => {
|
|
205
|
+
clearCloseTimer();
|
|
206
|
+
const delayMs = Math.max(0, leaveDelay.value);
|
|
207
|
+
if (delayMs === 0) {
|
|
208
|
+
close();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
closeTimer = setTimeout(() => {
|
|
212
|
+
closeTimer = null;
|
|
213
|
+
close();
|
|
214
|
+
}, delayMs);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const isOverlayNode = (node: EventTarget | null) => {
|
|
218
|
+
const overlay = overlayElement.value;
|
|
219
|
+
return Boolean(overlay && node instanceof Node && overlay.contains(node));
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const isForeignOverlayNode = (node: EventTarget | null) => {
|
|
223
|
+
if (!(node instanceof Node)) return false;
|
|
224
|
+
const own = overlayElement.value;
|
|
225
|
+
for (const overlay of activeOverlays) {
|
|
226
|
+
if (overlay !== own && overlay.contains(node)) return true;
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const onOverlayMouseOver = (event: MouseEvent) => {
|
|
232
|
+
if (!isOverlayNode(event.target)) return;
|
|
233
|
+
clearCloseTimer();
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const onOverlayMouseOut = (event: MouseEvent) => {
|
|
237
|
+
if (!isOverlayNode(event.target)) return;
|
|
238
|
+
const related = event.relatedTarget;
|
|
239
|
+
if (isOverlayNode(related)) return;
|
|
240
|
+
if (isForeignOverlayNode(related)) {
|
|
241
|
+
close();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (activeCell && related instanceof Node && activeCell.contains(related)) {
|
|
245
|
+
const relatedRoot = getEventRoot(related);
|
|
246
|
+
if (!relatedRoot || relatedRoot === rootRef.value) return;
|
|
247
|
+
close();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
scheduleClose();
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
let overlayListening = false;
|
|
254
|
+
|
|
255
|
+
const addOverlayListeners = () => {
|
|
256
|
+
if (overlayListening || typeof document === 'undefined') return;
|
|
257
|
+
overlayListening = true;
|
|
258
|
+
document.addEventListener('mouseover', onOverlayMouseOver);
|
|
259
|
+
document.addEventListener('mouseout', onOverlayMouseOut);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const removeOverlayListeners = () => {
|
|
263
|
+
if (!overlayListening) return;
|
|
264
|
+
overlayListening = false;
|
|
265
|
+
document.removeEventListener('mouseover', onOverlayMouseOver);
|
|
266
|
+
document.removeEventListener('mouseout', onOverlayMouseOut);
|
|
123
267
|
};
|
|
124
268
|
|
|
125
269
|
const getCellFromEvent = (event: MouseEvent) => {
|
|
@@ -140,24 +284,32 @@ export function useEllipsisTooltip(
|
|
|
140
284
|
close();
|
|
141
285
|
const measurement = measureCell(cell);
|
|
142
286
|
if (!measurement.overflow) return;
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
left: `${rect.left}px`,
|
|
149
|
-
top: `${rect.top}px`,
|
|
150
|
-
width: `${rect.width}px`,
|
|
151
|
-
height: `${rect.height}px`,
|
|
152
|
-
};
|
|
287
|
+
const contentRect = measureContentRect(cell);
|
|
288
|
+
if (!contentRect.width || !contentRect.height) return;
|
|
289
|
+
const rect = measureTextRect(cell, contentRect);
|
|
290
|
+
pendingContent = measurement.text;
|
|
291
|
+
pendingRect = rect;
|
|
153
292
|
pendingCell = cell;
|
|
154
293
|
openTimer = setTimeout(
|
|
155
294
|
() => {
|
|
156
295
|
openTimer = null;
|
|
157
296
|
pendingCell = null;
|
|
158
|
-
|
|
297
|
+
const anchorRect = pendingRect;
|
|
298
|
+
const nextContent = pendingContent;
|
|
299
|
+
pendingContent = '';
|
|
300
|
+
pendingRect = null;
|
|
301
|
+
if (!cell.isConnected || !anchorRect) return;
|
|
302
|
+
content.value = nextContent;
|
|
303
|
+
anchorStyle.value = {
|
|
304
|
+
...ANCHOR_BASE_STYLE,
|
|
305
|
+
left: `${anchorRect.left}px`,
|
|
306
|
+
top: `${anchorRect.top}px`,
|
|
307
|
+
width: `${anchorRect.width}px`,
|
|
308
|
+
height: `${anchorRect.height}px`,
|
|
309
|
+
};
|
|
159
310
|
activeCell = cell;
|
|
160
311
|
open.value = true;
|
|
312
|
+
addOverlayListeners();
|
|
161
313
|
},
|
|
162
314
|
Math.max(0, delay.value)
|
|
163
315
|
);
|
|
@@ -171,6 +323,11 @@ export function useEllipsisTooltip(
|
|
|
171
323
|
if (!owner || !target || !owner.contains(target)) return;
|
|
172
324
|
|
|
173
325
|
const related = event.relatedTarget as Node | null;
|
|
326
|
+
if (isOverlayNode(related)) return;
|
|
327
|
+
if (isForeignOverlayNode(related)) {
|
|
328
|
+
close();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
174
331
|
if (related && owner.contains(related)) {
|
|
175
332
|
const relatedRoot = getEventRoot(related);
|
|
176
333
|
if (!relatedRoot || relatedRoot === rootRef.value) return;
|
|
@@ -183,16 +340,7 @@ export function useEllipsisTooltip(
|
|
|
183
340
|
return;
|
|
184
341
|
}
|
|
185
342
|
|
|
186
|
-
|
|
187
|
-
const delayMs = Math.max(0, leaveDelay.value);
|
|
188
|
-
if (delayMs === 0) {
|
|
189
|
-
close();
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
closeTimer = setTimeout(() => {
|
|
193
|
-
closeTimer = null;
|
|
194
|
-
close();
|
|
195
|
-
}, delayMs);
|
|
343
|
+
scheduleClose();
|
|
196
344
|
};
|
|
197
345
|
|
|
198
346
|
const onViewportChange = () => close();
|
|
@@ -229,6 +377,7 @@ export function useEllipsisTooltip(
|
|
|
229
377
|
|
|
230
378
|
onBeforeUnmount(() => {
|
|
231
379
|
removeViewportListeners();
|
|
380
|
+
removeOverlayListeners();
|
|
232
381
|
clearOpenTimer();
|
|
233
382
|
clearCloseTimer();
|
|
234
383
|
});
|