PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / trunk
Secure Custom Fields vtrunk
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / assets / src / js / pro / blocks-v3 / components / block-toolbar-fields.js
secure-custom-fields / assets / src / js / pro / blocks-v3 / components Last commit date
block-edit.js 3 weeks ago block-form.js 2 months ago block-placeholder.js 6 months ago block-preview.js 6 months ago block-toolbar-fields.js 2 months ago error-boundary.js 2 months ago inline-editing-toolbar.js 6 months ago jsx-parser.js 2 months ago popover-wrapper.js 2 months ago
block-toolbar-fields.js
263 lines
1 /**
2 * BlockToolbarFields Component
3 * Renders field buttons in the WordPress block toolbar (top toolbar)
4 * Allows quick access to edit specific fields from the block toolbar
5 */
6
7 /* global acf */
8
9 import { useState, useRef } from '@wordpress/element';
10 import { BlockControls } from '@wordpress/blockEditor';
11 import { ToolbarGroup, ToolbarButton, Modal } from '@wordpress/components';
12 import { PopoverWrapper } from './popover-wrapper';
13
14 /**
15 * BlockToolbarFields component
16 * Displays field editing buttons in the WordPress block toolbar
17 *
18 * @param {Object} props - Component props
19 * @param {Array} props.blockToolbarFields - Array of field names or field config objects to show in toolbar
20 * @param {Array} props.blockFieldInfo - Array of all field information
21 * @param {Function} props.setCurrentBlockFormContainer - Setter for form container ref
22 * @param {Document|HTMLIFrameElement} props.gutenbergIframeOrDocument - Document or iframe reference
23 * @param {Function} props.setBlockFormModalOpen - Setter for block form modal state
24 * @param {boolean} props.blockFormModalOpen - Whether block form modal is open
25 * @param {boolean} props.hideExpandedEditorBtnInToolbar - Whether to hide the expanded editor button in the toolbar
26 * @return {JSX.Element} - Rendered toolbar controls
27 */
28 export const BlockToolbarFields = ( {
29 blockToolbarFields,
30 blockFieldInfo,
31 setCurrentBlockFormContainer,
32 gutenbergIframeOrDocument,
33 setBlockFormModalOpen,
34 blockFormModalOpen,
35 hideExpandedEditorBtnInToolbar,
36 } ) => {
37 const [ selectedFieldKey, setSelectedFieldKey ] = useState( null );
38 const [ selectedFieldButtonRef, setSelectedFieldButtonRef ] = useState();
39 const [ usePopover, setUsePopover ] = useState( true );
40 const fieldPopoverContainerRef = useRef();
41
42 /**
43 * Get field type class name from field name
44 *
45 * @param {string} fieldName Field name.
46 * @return {string} Field type class name.
47 */
48 const getFieldTypeClassName = ( fieldName ) => {
49 if ( ! fieldName || ! blockFieldInfo ) {
50 return '';
51 }
52 const field = blockFieldInfo.find( ( f ) => f.name === fieldName );
53 return field?.type ? field.type.replace( /_/g, '-' ) : '';
54 };
55
56 /**
57 * Get field info object from field name
58 *
59 * @param {string} fieldName Field name.
60 * @return {Object|null} Field info object.
61 */
62 const getFieldInfo = ( fieldName ) => {
63 return fieldName && blockFieldInfo
64 ? blockFieldInfo.find( ( f ) => f.name === fieldName )
65 : null;
66 };
67
68 /**
69 * Get field label from field name
70 *
71 * @param {string} fieldName Field name.
72 * @return {string} Field label.
73 */
74 const getFieldLabel = ( fieldName ) => {
75 if ( ! fieldName || ! blockFieldInfo ) {
76 return fieldName || '';
77 }
78 const field = blockFieldInfo.find( ( f ) => f.name === fieldName );
79 // Fallback to fieldName when label not found to ensure toolbar shows a title
80 return field?.label || fieldName || '';
81 };
82 return (
83 <BlockControls>
84 { /* Edit Block button */ }
85 { ! hideExpandedEditorBtnInToolbar && (
86 <ToolbarGroup>
87 <ToolbarButton
88 className="components-icon-button components-toolbar__control"
89 label={ acf.__( 'Edit Block' ) }
90 icon="edit"
91 onClick={ () => {
92 setBlockFormModalOpen( true );
93 } }
94 isPressed={ blockFormModalOpen }
95 />
96 </ToolbarGroup>
97 ) }
98
99 { /* Field buttons */ }
100 { blockToolbarFields.length > 0 && (
101 <ToolbarGroup>
102 { /* Style to show selected field in form */ }
103 { ( () => {
104 const styleContent = `[data-name="${ selectedFieldKey }"]{ display: block!important; }`;
105 return <style>{ styleContent }</style>;
106 } )() }
107
108 { /* Render field buttons */ }
109 { blockToolbarFields &&
110 blockToolbarFields.map( ( field ) => {
111 let fieldName = '';
112 let fieldIconSvg = null;
113 let fieldLabel = null;
114
115 // Handle field config object or simple string
116 if ( typeof field === 'object' ) {
117 fieldName = field.fieldName
118 ? field.fieldName
119 : field.index;
120 fieldIconSvg = field.fieldIcon
121 ? window.atob( field.fieldIcon )
122 : null;
123 fieldLabel = field.fieldLabel
124 ? field.fieldLabel
125 : fieldName;
126 } else {
127 fieldName = field;
128 }
129
130 return (
131 <ToolbarButton
132 key={ fieldName }
133 icon={
134 fieldIconSvg ? (
135 <i
136 dangerouslySetInnerHTML={ {
137 __html: fieldIconSvg,
138 } }
139 />
140 ) : (
141 <i
142 className={ `field-type-icon field-type-icon-${ getFieldTypeClassName(
143 fieldName
144 ) }` }
145 />
146 )
147 }
148 label={
149 fieldLabel || getFieldLabel( fieldName )
150 }
151 isPressed={ fieldName === selectedFieldKey }
152 ref={
153 fieldName === selectedFieldKey
154 ? setSelectedFieldButtonRef
155 : null
156 }
157 onMouseDown={ () => {
158 if ( selectedFieldKey === fieldName ) {
159 setSelectedFieldKey( null );
160 } else {
161 setSelectedFieldKey( null );
162 setTimeout( () => {
163 const fieldInfo =
164 getFieldInfo( fieldName );
165 // Use modal for complex field types
166 if (
167 fieldInfo?.type ===
168 'flexible_content' ||
169 fieldInfo?.type ===
170 'repeater'
171 ) {
172 setUsePopover( false );
173 } else {
174 setUsePopover( true );
175 }
176 setSelectedFieldKey(
177 fieldName
178 );
179 } );
180 }
181 } }
182 />
183 );
184 } ) }
185
186 { /* Popover for simple field types */ }
187 { selectedFieldKey &&
188 selectedFieldButtonRef &&
189 usePopover && (
190 <PopoverWrapper
191 focusOnMount={ false }
192 className="acf-inline-fields-popover"
193 anchor={ selectedFieldButtonRef }
194 animate={ true }
195 onClose={ ( event ) => {
196 // Don't close on certain events
197 if (
198 event.key !== 'Escape' &&
199 ( event?.target.closest(
200 '.media-modal'
201 ) ||
202 event?.target.closest(
203 '.acf-tooltip'
204 ) ||
205 ( event?.target &&
206 fieldPopoverContainerRef?.current &&
207 fieldPopoverContainerRef?.current.contains(
208 event.target
209 ) ) ||
210 ( selectedFieldButtonRef?.current &&
211 selectedFieldButtonRef?.current.contains(
212 event?.target
213 ) ) )
214 ) {
215 return false;
216 }
217 setSelectedFieldKey( null );
218 return true;
219 } }
220 variant="unstyled"
221 gutenbergIframeOrDocument={
222 gutenbergIframeOrDocument
223 }
224 >
225 <div ref={ fieldPopoverContainerRef }>
226 <div
227 className="acf-inline-fields-popover-inner"
228 style={ { minWidth: '300px' } }
229 ref={ setCurrentBlockFormContainer }
230 />
231 </div>
232 </PopoverWrapper>
233 ) }
234
235 { /* Modal for complex field types */ }
236 { selectedFieldKey &&
237 selectedFieldButtonRef &&
238 ! usePopover && (
239 <Modal
240 className="acf-block-form-modal"
241 isFullScreen={ true }
242 title={
243 getFieldInfo( selectedFieldKey )?.label
244 }
245 onRequestClose={ () => {
246 setSelectedFieldKey( null );
247 } }
248 >
249 <div ref={ fieldPopoverContainerRef }>
250 <div
251 className="acf-inline-fields-popover-inner"
252 style={ { minWidth: '300px' } }
253 ref={ setCurrentBlockFormContainer }
254 />
255 </div>
256 </Modal>
257 ) }
258 </ToolbarGroup>
259 ) }
260 </BlockControls>
261 );
262 };
263