PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.6.0
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.6.0
3.6.0 3.5.3 3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / src / style-customizer-v2 / HoverTip.tsx
everest-forms / src / style-customizer-v2 Last commit date
AiAssistant.tsx 2 weeks ago App.tsx 2 weeks ago BuilderSync.ts 2 weeks ago ControlRenderer.tsx 2 weeks ago HoverTip.tsx 2 weeks ago Popover.tsx 2 weeks ago PreviewBridge.ts 2 weeks ago PreviewPane.tsx 2 weeks ago constants.ts 2 weeks ago cssVars.ts 2 weeks ago globals.d.ts 2 weeks ago index.tsx 2 weeks ago panes.tsx 2 weeks ago store.ts 2 weeks ago style.scss 2 weeks ago types.ts 2 weeks ago
HoverTip.tsx
109 lines
1 /**
2 * Style Customizer v2 — hover/focus tooltip.
3 *
4 * A self-dismissing info bubble: opens on pointer-enter / keyboard focus of its trigger and
5 * closes on pointer-leave, blur, scroll, resize or Escape. Portaled into the panel root so it
6 * inherits the panel tokens and clears the sidebar's overflow, positioned against the trigger.
7 */
8 import React from 'react';
9 import { createPortal } from 'react-dom';
10
11 type Placement = 'top' | 'bottom';
12
13 function tipHost(): HTMLElement {
14 return document.getElementById( 'everest-forms-panel-style' ) || document.body;
15 }
16
17 export function HoverTip( {
18 tip,
19 label,
20 className,
21 children,
22 }: {
23 tip: React.ReactNode;
24 label: string;
25 className?: string;
26 children: React.ReactNode;
27 } ) {
28 const triggerRef = React.useRef< HTMLButtonElement >( null );
29 const tipRef = React.useRef< HTMLDivElement >( null );
30 const [ open, setOpen ] = React.useState( false );
31 const [ pos, setPos ] = React.useState< { left: number; top: number; placement: Placement } >( {
32 left: -9999,
33 top: -9999,
34 placement: 'bottom',
35 } );
36
37 React.useLayoutEffect( () => {
38 if ( ! open ) {
39 return;
40 }
41 const trigger = triggerRef.current;
42 const el = tipRef.current;
43 if ( ! trigger || ! el ) {
44 return;
45 }
46 const r = trigger.getBoundingClientRect();
47 const w = el.offsetWidth;
48 const h = el.offsetHeight;
49 let left = r.left + r.width / 2 - w / 2;
50 left = Math.min( Math.max( 8, left ), window.innerWidth - w - 8 );
51 let top = r.bottom + 8;
52 let placement: Placement = 'bottom';
53 if ( top + h > window.innerHeight - 8 ) {
54 top = r.top - h - 8;
55 placement = 'top';
56 }
57 setPos( { left, top: Math.max( 8, top ), placement } );
58 }, [ open, tip ] );
59
60 React.useEffect( () => {
61 if ( ! open ) {
62 return;
63 }
64 const close = () => setOpen( false );
65 const onKey = ( e: KeyboardEvent ) => {
66 if ( e.key === 'Escape' ) {
67 setOpen( false );
68 }
69 };
70 window.addEventListener( 'scroll', close, true );
71 window.addEventListener( 'resize', close );
72 document.addEventListener( 'keydown', onKey );
73 return () => {
74 window.removeEventListener( 'scroll', close, true );
75 window.removeEventListener( 'resize', close );
76 document.removeEventListener( 'keydown', onKey );
77 };
78 }, [ open ] );
79
80 return (
81 <>
82 <button
83 ref={ triggerRef }
84 type="button"
85 className={ className }
86 aria-label={ label }
87 onMouseEnter={ () => setOpen( true ) }
88 onMouseLeave={ () => setOpen( false ) }
89 onFocus={ () => setOpen( true ) }
90 onBlur={ () => setOpen( false ) }
91 >
92 { children }
93 </button>
94 { open &&
95 createPortal(
96 <div
97 ref={ tipRef }
98 className={ 'scv2-hovertip is-' + pos.placement }
99 role="tooltip"
100 style={ { left: pos.left, top: pos.top } }
101 >
102 { tip }
103 </div>,
104 tipHost()
105 ) }
106 </>
107 );
108 }
109