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 / bindings / block-editor.js
secure-custom-fields / assets / src / js / bindings Last commit date
block-editor.js 6 months ago constants.js 6 months ago field-processing.js 6 months ago fieldMetadataCache.js 6 months ago hooks.js 6 months ago index.js 11 months ago sources.js 6 months ago utils.js 6 months ago
block-editor.js
249 lines
1 /**
2 * WordPress dependencies
3 */
4 import { useCallback, useMemo } from '@wordpress/element';
5 import { addFilter } from '@wordpress/hooks';
6 import { createHigherOrderComponent } from '@wordpress/compose';
7 import {
8 InspectorControls,
9 useBlockBindingsUtils,
10 } from '@wordpress/block-editor';
11 import {
12 ComboboxControl,
13 __experimentalToolsPanel as ToolsPanel,
14 __experimentalToolsPanelItem as ToolsPanelItem,
15 } from '@wordpress/components';
16 import { __ } from '@wordpress/i18n';
17
18 /**
19 * Internal dependencies
20 */
21 import { BINDING_SOURCE } from './constants';
22 import {
23 getBindableAttributes,
24 getFilteredFieldOptions,
25 canUseUnifiedBinding,
26 fieldsToOptions,
27 } from './utils';
28 import {
29 useSiteEditorContext,
30 usePostEditorFields,
31 useSiteEditorFields,
32 useBoundFields,
33 } from './hooks';
34
35 /**
36 * Add custom block binding controls to supported blocks.
37 *
38 * @since 6.5.0
39 */
40 const withCustomControls = createHigherOrderComponent( ( BlockEdit ) => {
41 return ( props ) => {
42 const bindableAttributes = getBindableAttributes( props.name );
43 const { updateBlockBindings, removeAllBlockBindings } =
44 useBlockBindingsUtils();
45
46 // Get editor context
47 const { isSiteEditor, templatePostType } = useSiteEditorContext();
48
49 // Get fields based on editor context
50 const postEditorFields = usePostEditorFields();
51 const { fields: siteEditorFields } =
52 useSiteEditorFields( templatePostType );
53
54 // Use appropriate fields based on context
55 const activeFields = isSiteEditor ? siteEditorFields : postEditorFields;
56
57 // Convert fields to options format
58 const allFieldOptions = useMemo(
59 () => fieldsToOptions( activeFields ),
60 [ activeFields ]
61 );
62
63 // Track bound fields
64 const { boundFields, setBoundFields } = useBoundFields(
65 props.attributes
66 );
67
68 // Get filtered field options for a specific attribute
69 const getAttributeFieldOptions = useCallback(
70 ( attribute = null ) => {
71 return getFilteredFieldOptions(
72 allFieldOptions,
73 props.name,
74 attribute
75 );
76 },
77 [ allFieldOptions, props.name ]
78 );
79
80 // Check if all attributes can use unified binding mode
81 const canUseAllAttributesMode = useMemo(
82 () => canUseUnifiedBinding( props.name, bindableAttributes ),
83 [ props.name, bindableAttributes ]
84 );
85
86 // Handle field selection changes
87 const handleFieldChange = useCallback(
88 ( attribute, value ) => {
89 if ( Array.isArray( attribute ) ) {
90 // Handle multiple attributes at once
91 const newBoundFields = { ...boundFields };
92 const bindings = {};
93
94 attribute.forEach( ( attr ) => {
95 newBoundFields[ attr ] = value;
96 bindings[ attr ] = value
97 ? {
98 source: BINDING_SOURCE,
99 args: { key: value },
100 }
101 : undefined;
102 } );
103
104 setBoundFields( newBoundFields );
105 updateBlockBindings( bindings );
106 } else {
107 // Handle single attribute
108 setBoundFields( ( prev ) => ( {
109 ...prev,
110 [ attribute ]: value,
111 } ) );
112 updateBlockBindings( {
113 [ attribute ]: value
114 ? {
115 source: BINDING_SOURCE,
116 args: { key: value },
117 }
118 : undefined,
119 } );
120 }
121 },
122 [ boundFields, setBoundFields, updateBlockBindings ]
123 );
124
125 // Handle reset all bindings
126 const handleReset = useCallback( () => {
127 removeAllBlockBindings();
128 setBoundFields( {} );
129 }, [ removeAllBlockBindings, setBoundFields ] );
130
131 // Determine if we should show the panel
132 const shouldShowPanel = useMemo( () => {
133 // In site editor, show panel if block has bindable attributes
134 if ( isSiteEditor ) {
135 return bindableAttributes && bindableAttributes.length > 0;
136 }
137 // In post editor, only show if we have fields available
138 return (
139 allFieldOptions.length > 0 &&
140 bindableAttributes &&
141 bindableAttributes.length > 0
142 );
143 }, [ isSiteEditor, allFieldOptions, bindableAttributes ] );
144
145 if ( ! shouldShowPanel ) {
146 return <BlockEdit { ...props } />;
147 }
148
149 return (
150 <>
151 <InspectorControls { ...props }>
152 <ToolsPanel
153 label={ __(
154 'Connect to a field',
155 'secure-custom-fields'
156 ) }
157 resetAll={ handleReset }
158 >
159 { canUseAllAttributesMode ? (
160 <ToolsPanelItem
161 hasValue={ () =>
162 !! boundFields[ bindableAttributes[ 0 ] ]
163 }
164 label={ __(
165 'All attributes',
166 'secure-custom-fields'
167 ) }
168 onDeselect={ () =>
169 handleFieldChange(
170 bindableAttributes,
171 null
172 )
173 }
174 isShownByDefault
175 >
176 <ComboboxControl
177 label={ __(
178 'Field',
179 'secure-custom-fields'
180 ) }
181 placeholder={ __(
182 'Select a field',
183 'secure-custom-fields'
184 ) }
185 options={ getAttributeFieldOptions() }
186 value={
187 boundFields[
188 bindableAttributes[ 0 ]
189 ] || ''
190 }
191 onChange={ ( value ) =>
192 handleFieldChange(
193 bindableAttributes,
194 value
195 )
196 }
197 __next40pxDefaultSize
198 __nextHasNoMarginBottom
199 />
200 </ToolsPanelItem>
201 ) : (
202 bindableAttributes.map( ( attribute ) => (
203 <ToolsPanelItem
204 key={ `scf-binding-${ attribute }` }
205 hasValue={ () =>
206 !! boundFields[ attribute ]
207 }
208 label={ attribute }
209 onDeselect={ () =>
210 handleFieldChange( attribute, null )
211 }
212 isShownByDefault
213 >
214 <ComboboxControl
215 label={ attribute }
216 placeholder={ __(
217 'Select a field',
218 'secure-custom-fields'
219 ) }
220 options={ getAttributeFieldOptions(
221 attribute
222 ) }
223 value={ boundFields[ attribute ] || '' }
224 onChange={ ( value ) =>
225 handleFieldChange(
226 attribute,
227 value
228 )
229 }
230 __next40pxDefaultSize
231 __nextHasNoMarginBottom
232 />
233 </ToolsPanelItem>
234 ) )
235 ) }
236 </ToolsPanel>
237 </InspectorControls>
238 <BlockEdit { ...props } />
239 </>
240 );
241 };
242 }, 'withCustomControls' );
243
244 addFilter(
245 'editor.BlockEdit',
246 'secure-custom-fields/with-custom-controls',
247 withCustomControls
248 );
249