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

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

104 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Multi-Step Forms document sidebar: pagination, tabs, tab titles.
3 * Step count comes from `msf-step-break` blocks; meta via `wppb_fb_msf_*`.
4 */
5 import { __ } from '@wordpress/i18n';
6 import { registerPlugin } from '@wordpress/plugins';
7 import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
8 import { ToggleControl, TextControl, Notice } from '@wordpress/components';
9 import { useEntityProp } from '@wordpress/core-data';
10 import { useSelect } from '@wordpress/data';
11
12 import { countStepBoundaries } from '../lib/stepBoundaries';
13
14 // Inner component runs the meta + block-tree hooks. Conditionally rendered by
15 // the outer guard so those hooks never fire on non-PB CPTs — and so the hook
16 // order is invariant per component (Rules of Hooks): the outer always calls
17 // exactly one hook, and the inner always calls its full hook set before any
18 // early return. Mirrors the RepeaterScopeGuard split-component pattern.
19 const MsfPanelInner = ( { postType } ) => {
20 const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );
21
22 // Step count = (real step boundaries) + 1. Each boundary closes one step
23 // and opens the next; the form always has at least one.
24 //
25 // `countStepBoundaries` filters injected progress bars out FIRST. // them would mean enabling a "Top of Form" bar shifted a
26 // leading step-break to index 1, so it stopped looking leading and the step
27 // count jumped by one — the tab-title list gained a phantom step.
28 const breakCount = useSelect( ( select ) => {
29 const editor = select( 'core/block-editor' );
30 if ( ! editor ) return 0;
31
32 return countStepBoundaries( editor.getBlocks() );
33 }, [] );
34
35 // All hooks are above this guard, so it gates only the JSX (not hook order).
36 if ( ! meta ) return null;
37
38 const pagination = meta.wppb_fb_msf_pagination || '';
39 const tabs = meta.wppb_fb_msf_tabs || '';
40 const tabTitles = Array.isArray( meta.wppb_fb_msf_tab_titles ) ? meta.wppb_fb_msf_tab_titles : [];
41 const stepCount = breakCount + 1;
42
43 const setTitle = ( i, val ) => {
44 const next = tabTitles.slice( 0, stepCount );
45 while ( next.length < stepCount ) next.push( '' );
46 next[ i ] = val;
47 setMeta( { wppb_fb_msf_tab_titles: next } );
48 };
49
50 return (
51 <PluginDocumentSettingPanel
52 name="wppb-msf-settings"
53 title={ __( 'Multi-Step Forms', 'profile-builder' ) }
54 >
55 { breakCount === 0 && (
56 <Notice status="info" isDismissible={ false }>
57 { __( 'Insert a "Step Break" block between fields to split this form into steps. Pagination and tabs become active once at least one step break is in place.', 'profile-builder' ) }
58 </Notice>
59 ) }
60
61 <ToggleControl
62 label={ __( 'Show pagination', 'profile-builder' ) }
63 checked={ pagination === 'yes' }
64 onChange={ ( on ) => setMeta( { wppb_fb_msf_pagination: on ? 'yes' : 'no' } ) }
65 help={ __( 'Display numeric step pagination next to the Previous / Next buttons.', 'profile-builder' ) }
66 />
67
68 <ToggleControl
69 label={ __( 'Show step tabs', 'profile-builder' ) }
70 checked={ tabs === 'yes' }
71 onChange={ ( on ) => setMeta( { wppb_fb_msf_tabs: on ? 'yes' : 'no' } ) }
72 help={ __( 'Display a tabbed step header above the form. Tab labels are configurable below.', 'profile-builder' ) }
73 />
74
75 { tabs === 'yes' && breakCount > 0 && (
76 <div style={ { marginTop: '12px' } }>
77 <p style={ { fontWeight: 600, marginBottom: '8px' } }>
78 { __( 'Tab Titles', 'profile-builder' ) }
79 </p>
80 { Array.from( { length: stepCount } ).map( ( _, i ) => (
81 <TextControl
82 key={ i }
83 label={ __( 'Step', 'profile-builder' ) + ' ' + ( i + 1 ) }
84 value={ tabTitles[ i ] || '' }
85 onChange={ ( val ) => setTitle( i, val ) }
86 placeholder={ __( 'Step', 'profile-builder' ) + ' ' + ( i + 1 ) }
87 />
88 ) ) }
89 </div>
90 ) }
91 </PluginDocumentSettingPanel>
92 );
93 };
94
95 const MsfPanel = () => {
96 const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] );
97 if ( ! [ 'wppb-rf-cpt', 'wppb-epf-cpt' ].includes( postType ) ) return null;
98 // Add-on bridge must be present (`window.wppbFb.msf`) or REST drops the meta.
99 if ( ! ( window.wppbFb && window.wppbFb.msf ) ) return null;
100 return <MsfPanelInner postType={ postType } />;
101 };
102
103 registerPlugin( 'wppb-msf-settings-plugin', { render: MsfPanel } );
104