PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / form-builder / blocks / src / components / UniqueFieldTooltip.js

UniqueFieldTooltip.js in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.3, at form-builder/blocks/src/components/UniqueFieldTooltip.js

100 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Tooltip on greyed-out once-per-form "New Fields" inserter entries.
3 * Document-level listener (sidebar + mini-inserter popovers). Only uniqueBlocks.
4 */
5
6 import { useEffect, useState } from '@wordpress/element';
7 import { useSelect } from '@wordpress/data';
8 import { registerPlugin } from '@wordpress/plugins';
9 import { Popover } from '@wordpress/components';
10 import { __, sprintf } from '@wordpress/i18n';
11
12 const PB_CPTS = [ 'wppb-rf-cpt', 'wppb-epf-cpt' ];
13 const ITEM_SELECTOR = '.block-editor-block-types-list__item';
14 const TITLE_SELECTOR = '.block-editor-block-types-list__item-title';
15
16 /**
17 * Inserter item classes for once-per-form blocks (`editor-block-list-item-{name}`).
18 * Built from names; class→name reverse is ambiguous (names have dashes).
19 *
20 * @return {Set<string>}
21 */
22 const uniqueItemClasses = () => {
23 const names = window.wppbFb?.uniqueness?.uniqueBlocks;
24 return new Set(
25 Array.isArray( names )
26 ? names.map( ( name ) => `editor-block-list-item-${ name.replace( '/', '-' ) }` )
27 : []
28 );
29 };
30
31 // Core marks the entry `aria-disabled` (Composite's accessibleWhenDisabled), not
32 // `disabled` — which is exactly why it still receives hover and focus. The
33 // `disabled` half is a guard in case a future version switches back.
34 const isDimmed = ( item ) => item.disabled || item.getAttribute( 'aria-disabled' ) === 'true';
35
36 const UniqueFieldTooltip = () => {
37 const isOurPostType = useSelect(
38 ( select ) => PB_CPTS.includes( select( 'core/editor' ).getCurrentPostType() ),
39 []
40 );
41 const [ anchor, setAnchor ] = useState( null );
42
43 useEffect( () => {
44 if ( ! isOurPostType ) return undefined;
45
46 const classes = uniqueItemClasses();
47 if ( ! classes.size ) return undefined;
48
49 const resolve = ( target ) => {
50 const item = target?.closest?.( ITEM_SELECTOR );
51 if ( ! item || ! isDimmed( item ) ) return null;
52 return Array.from( item.classList ).some( ( cls ) => classes.has( cls ) ) ? item : null;
53 };
54
55 // `mouseover` (not `mouseenter`) so one document-level listener sees
56 // every entry; it re-fires on each element the pointer enters, so
57 // leaving an entry for anything else resolves to null and closes.
58 const onPoint = ( event ) => setAnchor( resolve( event.target ) );
59 const onLeaveWindow = () => setAnchor( null );
60
61 document.addEventListener( 'mouseover', onPoint );
62 document.addEventListener( 'focusin', onPoint );
63 document.documentElement.addEventListener( 'mouseleave', onLeaveWindow );
64
65 return () => {
66 document.removeEventListener( 'mouseover', onPoint );
67 document.removeEventListener( 'focusin', onPoint );
68 document.documentElement.removeEventListener( 'mouseleave', onLeaveWindow );
69 };
70 }, [ isOurPostType ] );
71
72 // `isConnected`: the entry can be re-rendered out from under a hover (the
73 // list rebuilds whenever the canvas changes), and positioning against a
74 // detached node would strand the tooltip at the top-left of the screen.
75 if ( ! anchor || ! anchor.isConnected ) return null;
76
77 const title = anchor.querySelector( TITLE_SELECTOR )?.textContent?.trim();
78
79 return (
80 <Popover
81 className="wppb-fb-unique-tip"
82 anchor={ anchor }
83 placement="bottom"
84 offset={ 8 }
85 focusOnMount={ false }
86 animate={ false }
87 >
88 { title
89 ? sprintf(
90 /* translators: %s: field name, e.g. "Username". */
91 __( '“%s” is already in this form. This field can only be added once per form.', 'profile-builder' ),
92 title
93 )
94 : __( 'This field is already in this form. It can only be added once per form.', 'profile-builder' ) }
95 </Popover>
96 );
97 };
98
99 registerPlugin( 'wppb-fb-unique-field-tooltip', { render: UniqueFieldTooltip } );
100