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 / CanvasPaddingDeselect.js

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

112 lines 4.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Click empty canvas padding to deselect (core's padding appender eats the event
3 * and would insert `core/paragraph`, which we disallow). Deselect only.
4 */
5 import { store as blockEditorStore } from '@wordpress/block-editor';
6 import { select as dataSelect, useDispatch, useSelect } from '@wordpress/data';
7 import { useEffect } from '@wordpress/element';
8 import { registerPlugin } from '@wordpress/plugins';
9
10 const PB_CPTS = [ 'wppb-rf-cpt', 'wppb-epf-cpt' ];
11
12 /**
13 * True when the click hit canvas empty space (styles wrapper, or `<html>` in iframe).
14 *
15 * @param {EventTarget} target
16 * @return {boolean}
17 */
18 const isCanvasEmptySpace = ( target ) => {
19 const doc = target && target.ownerDocument;
20 if ( ! doc ) return false;
21
22 const wrapper = doc.querySelector( '.editor-styles-wrapper' );
23 if ( ! wrapper ) return false;
24 if ( target === wrapper ) return true;
25
26 // Only when the canvas is iframed: there, `<html>` is part of the canvas.
27 // In the non-iframed fallback the wrapper is a plain div and everything
28 // outside it is editor chrome, which must not deselect.
29 return wrapper === doc.body && target === doc.documentElement;
30 };
31
32 const CanvasPaddingDeselect = () => {
33 const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] );
34 const isOurPostType = PB_CPTS.includes( postType );
35
36 const { clearSelectedBlock } = useDispatch( blockEditorStore );
37
38 useEffect( () => {
39 if ( ! isOurPostType ) return undefined;
40
41 const onPointerDown = ( event ) => {
42 // Primary button only — a right-click opens a context menu and
43 // shouldn't move the selection.
44 if ( event.button !== 0 ) return;
45 if ( ! isCanvasEmptySpace( event.target ) ) return;
46
47 const editor = dataSelect( blockEditorStore );
48 if ( ! editor.getSelectedBlockClientId() && ! editor.hasMultiSelection() ) return;
49
50 clearSelectedBlock();
51
52 // Clearing the store isn't enough: the same `preventDefault()` also
53 // cancels the browser's focus shift, and core paints a selection
54 // outline off `:focus` alone, so the field would still LOOK selected.
55 // `blur()` hands focus to the canvas `<body>` — where a click on the
56 // canvas' sides leaves it anyway.
57 const doc = event.target.ownerDocument;
58 const active = doc.activeElement;
59 if ( active && active !== doc.body && typeof active.blur === 'function' ) {
60 active.blur();
61 }
62 };
63
64 let attachedDoc = null;
65 let frame = 0;
66
67 const sync = () => {
68 const iframe = document.querySelector( 'iframe[name="editor-canvas"]' );
69 // `document` fallback covers a non-iframed canvas (device preview /
70 // older Gutenberg), as in RootCanvasAppender.
71 const doc = iframe ? iframe.contentDocument : document;
72 if ( doc === attachedDoc ) return;
73
74 if ( attachedDoc ) {
75 attachedDoc.removeEventListener( 'pointerdown', onPointerDown, true );
76 }
77 attachedDoc = doc;
78 if ( doc ) {
79 doc.addEventListener( 'pointerdown', onPointerDown, true );
80 }
81 };
82
83 const schedule = () => {
84 if ( frame ) return;
85 frame = window.requestAnimationFrame( () => {
86 frame = 0;
87 sync();
88 } );
89 };
90
91 sync();
92 // The canvas iframe is recreated on device-preview switches, which
93 // replaces the document we're attached to.
94 const observer = new MutationObserver( schedule );
95 observer.observe( document.body, { childList: true, subtree: true } );
96
97 return () => {
98 observer.disconnect();
99 if ( frame ) window.cancelAnimationFrame( frame );
100 if ( attachedDoc ) {
101 attachedDoc.removeEventListener( 'pointerdown', onPointerDown, true );
102 }
103 };
104 }, [ isOurPostType, clearSelectedBlock ] );
105
106 return null;
107 };
108
109 registerPlugin( 'wppb-fb-canvas-padding-deselect', { render: CanvasPaddingDeselect } );
110
111 export default CanvasPaddingDeselect;
112