1- import Trigger from '@rc-component/trigger' ;
1+ import Trigger , { type TriggerRef } from '@rc-component/trigger' ;
22import React from 'react' ;
3+ import {
4+ getSafeHoverAreaPolygons ,
5+ type SafeHoverPoint ,
6+ } from '../../src/util/safeHover' ;
37import '../../assets/index.less' ;
48
9+ type SafeHoverPolygon = {
10+ points : SafeHoverPoint [ ] ;
11+ fill : string ;
12+ stroke : string ;
13+ } ;
14+
15+ const safeHoverPolygonStyles = [
16+ {
17+ fill : 'rgba(255, 176, 32, 0.22)' ,
18+ stroke : 'rgba(222, 121, 0, 0.6)' ,
19+ } ,
20+ {
21+ fill : 'rgba(22, 119, 255, 0.16)' ,
22+ stroke : 'rgba(22, 119, 255, 0.55)' ,
23+ } ,
24+ ] ;
25+
526const builtinPlacements = {
627 top : {
728 points : [ 'bc' , 'tc' ] ,
@@ -18,16 +39,77 @@ const popupStyle: React.CSSProperties = {
1839} ;
1940
2041const SafeHoverDemo = ( ) => {
42+ const triggerRef = React . useRef < TriggerRef > ( null ) ;
43+
44+ const [ safeHoverPolygons , setSafeHoverPolygons ] = React . useState <
45+ SafeHoverPolygon [ ]
46+ > ( [ ] ) ;
47+
48+ const updateSafeHoverPolygons = (
49+ event : React . MouseEvent < HTMLElement > | React . PointerEvent < HTMLElement > ,
50+ ) => {
51+ const target = triggerRef . current ?. nativeElement ;
52+ const popup = triggerRef . current ?. popupElement ;
53+
54+ if ( ! target || ! popup ) {
55+ setSafeHoverPolygons ( [ ] ) ;
56+ return ;
57+ }
58+
59+ const leavePoint : SafeHoverPoint = [ event . clientX , event . clientY ] ;
60+ setSafeHoverPolygons (
61+ getSafeHoverAreaPolygons (
62+ leavePoint ,
63+ target . getBoundingClientRect ( ) ,
64+ popup . getBoundingClientRect ( ) ,
65+ ) . map ( ( points , index ) => ( {
66+ points,
67+ ...safeHoverPolygonStyles [ index ] ,
68+ } ) ) ,
69+ ) ;
70+ } ;
71+
2172 return (
2273 < div style = { { minHeight : 320 , padding : '160px 80px 80px' } } >
74+ { safeHoverPolygons . length > 0 && (
75+ < svg
76+ aria-hidden
77+ style = { {
78+ position : 'fixed' ,
79+ inset : 0 ,
80+ width : '100vw' ,
81+ height : '100vh' ,
82+ pointerEvents : 'none' ,
83+ zIndex : 999 ,
84+ } }
85+ >
86+ { safeHoverPolygons . map ( ( { points, fill, stroke } , index ) => (
87+ < polygon
88+ // eslint-disable-next-line react/no-array-index-key
89+ key = { index }
90+ points = { points . map ( ( point ) => point . join ( ',' ) ) . join ( ' ' ) }
91+ fill = { fill }
92+ stroke = { stroke }
93+ strokeDasharray = "4 3"
94+ strokeWidth = { 1 }
95+ />
96+ ) ) }
97+ </ svg >
98+ ) }
2399 < Trigger
100+ ref = { triggerRef }
24101 action = { [ 'hover' ] }
25102 mouseLeaveDelay = { 0.12 }
26103 popupPlacement = "top"
27104 builtinPlacements = { builtinPlacements }
28105 popupStyle = { popupStyle }
106+ onOpenChange = { ( nextOpen ) => {
107+ if ( ! nextOpen ) {
108+ setSafeHoverPolygons ( [ ] ) ;
109+ }
110+ } }
29111 popup = {
30- < div >
112+ < div onMouseEnter = { ( ) => setSafeHoverPolygons ( [ ] ) } >
31113 < strong > Safe hover popup</ strong >
32114 < div style = { { marginTop : 8 } } >
33115 Move through the gap to reach me.
@@ -38,7 +120,12 @@ const SafeHoverDemo = () => {
38120 </ div >
39121 }
40122 >
41- < button style = { { padding : '8px 16px' } } type = "button" >
123+ < button
124+ style = { { padding : '8px 16px' } }
125+ type = "button"
126+ onMouseLeave = { updateSafeHoverPolygons }
127+ onPointerLeave = { updateSafeHoverPolygons }
128+ >
42129 Hover target
43130 </ button >
44131 </ Trigger >
0 commit comments