PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.0
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.0
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 / MiniInserterSearchFocus.js

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

122 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Focus the mini inserter search field when its popover opens.
3 *
4 * Inserter hardcodes popover focus with no seam; WP 7.0's first tabbable is the
5 * tab button, not search. Re-assert for a few frames (core steals focus once via
6 * setTimeout), then stop on real user input. Docked inserter and tab switches
7 * are left alone.
8 */
9 import { useSelect } from '@wordpress/data';
10 import { useEffect } from '@wordpress/element';
11 import { registerPlugin } from '@wordpress/plugins';
12
13 import { getMiniInserterTarget, subscribeMiniInserterTarget } from '../lib/miniInserterTarget';
14
15 const PB_CPTS = [ 'wppb-rf-cpt', 'wppb-epf-cpt' ];
16
17 const POPOVER_SELECTOR = '.block-editor-inserter__popover';
18
19 // Hard cap; only real frames count so a slow mount doesn't eat the budget.
20 const MAX_FRAMES = 12;
21
22 // Enough consecutive focused frames to outlast core's focus-first-tabbable timeout.
23 const SETTLED_FRAMES = 3;
24
25 /**
26 * Visible tab's search input (both panels stay mounted; inactive is `hidden`).
27 *
28 * @param {Element} popover The open inserter popover.
29 * @return {?HTMLInputElement} The visible search input, or null.
30 */
31 const visibleSearchInput = ( popover ) => {
32 for ( const search of popover.querySelectorAll( '.block-editor-inserter__search' ) ) {
33 if ( search.closest( '[hidden]' ) ) continue;
34 const input = search.querySelector( 'input' );
35 if ( input ) return input;
36 }
37 return null;
38 };
39
40 /**
41 * Keep search focused for the first few frames of the popover's life.
42 *
43 * @return {Function} Cancels the loop; idempotent.
44 */
45 const assertSearchFocus = () => {
46 let frames = 0;
47 let settled = 0;
48 let raf = 0;
49
50 const stop = () => {
51 if ( raf ) window.cancelAnimationFrame( raf );
52 raf = 0;
53 document.removeEventListener( 'pointerdown', stop, true );
54 document.removeEventListener( 'keydown', stop, true );
55 };
56
57 const tick = () => {
58 raf = 0;
59 if ( frames++ >= MAX_FRAMES || settled >= SETTLED_FRAMES ) {
60 stop();
61 return;
62 }
63
64 const popover = document.querySelector( POPOVER_SELECTOR );
65 const input = popover ? visibleSearchInput( popover ) : null;
66 if ( input ) {
67 if ( input.ownerDocument.activeElement === input ) {
68 settled++;
69 } else {
70 settled = 0;
71 // preventScroll: popover is still positioning; scroll-into-view yanks the canvas.
72 input.focus( { preventScroll: true } );
73 }
74 }
75
76 raf = window.requestAnimationFrame( tick );
77 };
78
79 document.addEventListener( 'pointerdown', stop, true );
80 document.addEventListener( 'keydown', stop, true );
81 raf = window.requestAnimationFrame( tick );
82
83 return stop;
84 };
85
86 const MiniInserterSearchFocus = () => {
87 const isOurPostType = useSelect(
88 ( select ) => PB_CPTS.includes( select( 'core/editor' ).getCurrentPostType() ),
89 []
90 );
91
92 useEffect( () => {
93 if ( ! isOurPostType ) return undefined;
94
95 // Destination store already published by AddFieldButton / CanvasPointInserter.
96 let cancel = null;
97 const onTargetChange = () => {
98 if ( ! getMiniInserterTarget().open ) {
99 if ( cancel ) cancel();
100 cancel = null;
101 return;
102 }
103 // Destination change is not a reopen — don't restart the loop.
104 if ( ! cancel ) cancel = assertSearchFocus();
105 };
106
107 const unsubscribe = subscribeMiniInserterTarget( onTargetChange );
108 onTargetChange();
109
110 return () => {
111 unsubscribe();
112 if ( cancel ) cancel();
113 };
114 }, [ isOurPostType ] );
115
116 return null;
117 };
118
119 registerPlugin( 'wppb-fb-mini-inserter-search-focus', { render: MiniInserterSearchFocus } );
120
121 export default MiniInserterSearchFocus;
122