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 / _browse-fields-modal.js
secure-custom-fields / assets / src / js Last commit date
bindings 6 months ago commands 3 weeks ago pro 3 weeks ago _acf-compatibility.js 1 year ago _acf-condition-types.js 8 months ago _acf-condition.js 1 year ago _acf-conditions.js 1 year ago _acf-field-accordion.js 6 months ago _acf-field-button-group.js 8 months ago _acf-field-checkbox.js 1 month ago _acf-field-color-picker.js 8 months ago _acf-field-date-picker.js 1 month ago _acf-field-date-time-picker.js 1 month ago _acf-field-file.js 1 month ago _acf-field-google-map.js 6 months ago _acf-field-icon-picker.js 1 month ago _acf-field-image.js 1 month ago _acf-field-link.js 1 year ago _acf-field-oembed.js 1 month ago _acf-field-page-link.js 1 year ago _acf-field-post-object.js 1 year ago _acf-field-radio.js 1 month ago _acf-field-range.js 1 year ago _acf-field-relationship.js 1 month ago _acf-field-select.js 1 month ago _acf-field-tab.js 1 month ago _acf-field-taxonomy.js 8 months ago _acf-field-time-picker.js 1 month ago _acf-field-true-false.js 1 month ago _acf-field-url.js 1 year ago _acf-field-user.js 1 year ago _acf-field-wysiwyg.js 1 month ago _acf-field.js 1 year ago _acf-fields.js 1 year ago _acf-helpers.js 1 year ago _acf-hooks.js 1 year ago _acf-internal-post-type.js 8 months ago _acf-media.js 3 weeks ago _acf-modal.js 1 year ago _acf-model.js 1 year ago _acf-notice.js 8 months ago _acf-panel.js 1 year ago _acf-popup.js 8 months ago _acf-postbox.js 1 year ago _acf-screen.js 10 months ago _acf-select2.js 1 year ago _acf-tinymce.js 6 months ago _acf-tooltip.js 10 months ago _acf-unload.js 1 year ago _acf-validation.js 1 month ago _acf.js 7 months ago _browse-fields-modal.js 8 months ago _field-group-compatibility.js 1 year ago _field-group-conditions.js 1 year ago _field-group-field.js 4 months ago _field-group-fields.js 10 months ago _field-group-locations.js 1 year ago _field-group-settings.js 1 year ago _field-group.js 10 months ago acf-escaped-html-notice.js 1 year ago acf-field-group.js 1 year ago acf-input.js 1 year ago acf-internal-post-type.js 1 year ago acf.js 1 year ago
_browse-fields-modal.js
381 lines
1 /**
2 * Extends acf.models.Modal to create the field browser.
3 */
4
5 ( function ( $, undefined, acf ) {
6 const browseFieldsModal = {
7 data: {
8 openedBy: null,
9 currentFieldType: null,
10 popularFieldTypes: [
11 'text',
12 'textarea',
13 'email',
14 'url',
15 'file',
16 'gallery',
17 'select',
18 'true_false',
19 'link',
20 'post_object',
21 'relationship',
22 'repeater',
23 'flexible_content',
24 'clone',
25 ],
26 },
27
28 events: {
29 'click .acf-modal-close': 'onClickClose',
30 'keydown .acf-browse-fields-modal': 'onPressEscapeClose',
31 'click .acf-select-field': 'onClickSelectField',
32 'click .acf-field-type': 'onClickFieldType',
33 'changed:currentFieldType': 'onChangeFieldType',
34 'input .acf-search-field-types': 'onSearchFieldTypes',
35 'click .acf-browse-popular-fields': 'onClickBrowsePopular',
36 },
37
38 setup: function ( props ) {
39 $.extend( this.data, props );
40 this.$el = $( this.tmpl() );
41 this.render();
42 },
43
44 initialize: function () {
45 this.open();
46 this.lockFocusToModal( true );
47 this.$el.find( '.acf-modal-title' ).trigger( 'focus' );
48 acf.doAction( 'show', this.$el );
49 },
50
51 tmpl: function () {
52 return $( '#tmpl-acf-browse-fields-modal' ).html();
53 },
54
55 getFieldTypes: function ( category, search ) {
56 let fieldTypes;
57 if ( ! acf.get( 'is_pro' ) ) {
58 // Add in the pro fields.
59 fieldTypes = Object.values( {
60 ...acf.get( 'fieldTypes' ),
61 ...acf.get( 'PROFieldTypes' ),
62 } );
63 } else {
64 fieldTypes = Object.values( acf.get( 'fieldTypes' ) );
65 }
66
67 if ( category ) {
68 if ( 'popular' === category ) {
69 return fieldTypes.filter( ( fieldType ) =>
70 this.get( 'popularFieldTypes' ).includes(
71 fieldType.name
72 )
73 );
74 }
75
76 if ( 'pro' === category ) {
77 return fieldTypes.filter( ( fieldType ) => fieldType.pro );
78 }
79
80 fieldTypes = fieldTypes.filter(
81 ( fieldType ) => fieldType.category === category
82 );
83 }
84
85 if ( search ) {
86 fieldTypes = fieldTypes.filter( ( fieldType ) => {
87 const label = fieldType.label.toLowerCase();
88 const labelParts = label.split( ' ' );
89 let match = false;
90
91 if ( label.startsWith( search.toLowerCase() ) ) {
92 match = true;
93 } else if ( labelParts.length > 1 ) {
94 labelParts.forEach( ( part ) => {
95 if ( part.startsWith( search.toLowerCase() ) ) {
96 match = true;
97 }
98 } );
99 }
100
101 return match;
102 } );
103 }
104
105 return fieldTypes;
106 },
107
108 render: function () {
109 acf.doAction( 'append', this.$el );
110
111 const $tabs = this.$el.find( '.acf-field-types-tab' );
112 const self = this;
113
114 $tabs.each( function () {
115 const category = $( this ).data( 'category' );
116 const fieldTypes = self.getFieldTypes( category );
117 fieldTypes.forEach( ( fieldType ) => {
118 $( this ).append( self.getFieldTypeHTML( fieldType ) );
119 } );
120 } );
121
122 this.initializeFieldLabel();
123 this.initializeFieldType();
124 this.onChangeFieldType();
125 },
126
127 getFieldTypeHTML: function ( fieldType ) {
128 const iconName = fieldType.name.replaceAll( '_', '-' );
129
130 return `
131 <a href="#" class="acf-field-type" data-field-type="${ fieldType.name }">
132 <i class="field-type-icon field-type-icon-${ iconName }"></i>
133 <span class="field-type-label">${ fieldType.label }</span>
134 </a>
135 `;
136 },
137
138 decodeFieldTypeURL: function ( url ) {
139 if ( typeof url != 'string' ) return url;
140 return url.replaceAll( '&#038;', '&' );
141 },
142
143 renderFieldTypeDesc: function ( fieldType ) {
144 const fieldTypeInfo =
145 this.getFieldTypes().filter(
146 ( fieldTypeFilter ) => fieldTypeFilter.name === fieldType
147 )[ 0 ] || {};
148
149 const args = acf.parseArgs( fieldTypeInfo, {
150 label: '',
151 description: '',
152 doc_url: false,
153 tutorial_url: false,
154 preview_image: false,
155 pro: false,
156 } );
157
158 this.$el.find( '.field-type-name' ).text( args.label );
159 this.$el.find( '.field-type-desc' ).text( args.description );
160
161 if ( args.doc_url ) {
162 this.$el
163 .find( '.field-type-doc' )
164 .attr( 'href', this.decodeFieldTypeURL( args.doc_url ) )
165 .show();
166 } else {
167 this.$el.find( '.field-type-doc' ).hide();
168 }
169
170 if ( args.tutorial_url ) {
171 this.$el
172 .find( '.field-type-tutorial' )
173 .attr(
174 'href',
175 this.decodeFieldTypeURL( args.tutorial_url )
176 )
177 .parent()
178 .show();
179 } else {
180 this.$el.find( '.field-type-tutorial' ).parent().hide();
181 }
182
183 if ( args.preview_image ) {
184 this.$el
185 .find( '.field-type-image' )
186 .attr( 'src', args.preview_image )
187 .show();
188 } else {
189 this.$el.find( '.field-type-image' ).hide();
190 }
191
192 const isPro = true;
193 const isActive = true;
194 const $upgateToProButton = this.$el.find( '.acf-btn-pro' );
195 const $upgradeToUnlockButton = this.$el.find(
196 '.field-type-upgrade-to-unlock'
197 );
198
199 if ( args.pro && ( ! isPro || ! isActive ) ) {
200 $upgateToProButton.show();
201 $upgateToProButton.attr(
202 'href',
203 $upgateToProButton.data( 'urlBase' ) + fieldType
204 );
205
206 $upgradeToUnlockButton.show();
207 $upgradeToUnlockButton.attr(
208 'href',
209 $upgradeToUnlockButton.data( 'urlBase' ) + fieldType
210 );
211 this.$el
212 .find( '.acf-insert-field-label' )
213 .attr( 'disabled', true );
214 this.$el.find( '.acf-select-field' ).hide();
215 } else {
216 $upgateToProButton.hide();
217 $upgradeToUnlockButton.hide();
218 this.$el
219 .find( '.acf-insert-field-label' )
220 .attr( 'disabled', false );
221 this.$el.find( '.acf-select-field' ).show();
222 }
223 },
224
225 initializeFieldType: function () {
226 const fieldObject = this.get( 'openedBy' );
227 const fieldType = fieldObject?.data?.type;
228
229 // Select default field type
230 if ( fieldType ) {
231 this.set( 'currentFieldType', fieldType );
232 } else {
233 this.set( 'currentFieldType', 'text' );
234 }
235
236 // Select first tab with selected field type
237 // If type selected is within Popular, select Popular Tab
238 // Else select first tab the type belongs
239 const fieldTypes = this.getFieldTypes();
240 const isFieldTypePopular =
241 this.get( 'popularFieldTypes' ).includes( fieldType );
242
243 let category = '';
244 if ( isFieldTypePopular ) {
245 category = 'popular';
246 } else {
247 const selectedFieldType = fieldTypes.find( ( x ) => {
248 return x.name === fieldType;
249 } );
250
251 category = selectedFieldType.category;
252 }
253
254 const uppercaseCategory =
255 category[ 0 ].toUpperCase() + category.slice( 1 );
256 const searchTabElement = `.acf-modal-content .acf-tab-wrap a:contains('${ uppercaseCategory }')`;
257 setTimeout( () => {
258 $( searchTabElement ).click();
259 }, 0 );
260 },
261
262 initializeFieldLabel: function () {
263 const fieldObject = this.get( 'openedBy' );
264 const labelText = fieldObject.$fieldLabel().val();
265 const $fieldLabel = this.$el.find( '.acf-insert-field-label' );
266 if ( labelText ) {
267 $fieldLabel.val( labelText );
268 } else {
269 $fieldLabel.val( '' );
270 }
271 },
272
273 updateFieldObjectFieldLabel: function () {
274 const label = this.$el.find( '.acf-insert-field-label' ).val();
275 const fieldObject = this.get( 'openedBy' );
276 fieldObject.$fieldLabel().val( label );
277 fieldObject.$fieldLabel().trigger( 'blur' );
278 },
279
280 onChangeFieldType: function () {
281 const fieldType = this.get( 'currentFieldType' );
282
283 this.$el.find( '.selected' ).removeClass( 'selected' );
284 this.$el
285 .find( '.acf-field-type[data-field-type="' + fieldType + '"]' )
286 .addClass( 'selected' );
287
288 this.renderFieldTypeDesc( fieldType );
289 },
290
291 onSearchFieldTypes: function ( e ) {
292 const $modal = this.$el.find( '.acf-browse-fields-modal' );
293 const inputVal = this.$el.find( '.acf-search-field-types' ).val();
294 const self = this;
295 let searchString,
296 resultsHtml = '';
297 let matches = [];
298
299 if ( 'string' === typeof inputVal ) {
300 searchString = inputVal.trim();
301 matches = this.getFieldTypes( false, searchString );
302 }
303
304 if ( searchString.length && matches.length ) {
305 $modal.addClass( 'is-searching' );
306 } else {
307 $modal.removeClass( 'is-searching' );
308 }
309
310 if ( ! matches.length ) {
311 $modal.addClass( 'no-results-found' );
312 this.$el
313 .find( '.acf-invalid-search-term' )
314 .text( searchString );
315 return;
316 } else {
317 $modal.removeClass( 'no-results-found' );
318 }
319
320 matches.forEach( ( fieldType ) => {
321 resultsHtml = resultsHtml + self.getFieldTypeHTML( fieldType );
322 } );
323
324 $( '.acf-field-type-search-results' ).html( resultsHtml );
325
326 this.set( 'currentFieldType', matches[ 0 ].name );
327 this.onChangeFieldType();
328 },
329
330 onClickBrowsePopular: function () {
331 this.$el
332 .find( '.acf-search-field-types' )
333 .val( '' )
334 .trigger( 'input' );
335 this.$el.find( '.acf-tab-wrap a' ).first().trigger( 'click' );
336 },
337
338 onClickSelectField: function ( e ) {
339 const fieldObject = this.get( 'openedBy' );
340
341 fieldObject
342 .$fieldTypeSelect()
343 .val( this.get( 'currentFieldType' ) );
344 fieldObject.$fieldTypeSelect().trigger( 'change' );
345
346 this.updateFieldObjectFieldLabel();
347
348 this.close();
349 },
350
351 onClickFieldType: function ( e ) {
352 const $fieldType = $( e.currentTarget );
353 this.set( 'currentFieldType', $fieldType.data( 'field-type' ) );
354 },
355
356 onClickClose: function () {
357 this.close();
358 },
359
360 onPressEscapeClose: function ( e ) {
361 if ( e.key === 'Escape' ) {
362 this.close();
363 }
364 },
365
366 close: function () {
367 this.lockFocusToModal( false );
368 this.returnFocusToOrigin();
369 this.remove();
370 },
371
372 focus: function () {
373 this.$el.find( 'button' ).first().trigger( 'focus' );
374 },
375 };
376
377 acf.models.browseFieldsModal = acf.models.Modal.extend( browseFieldsModal );
378 acf.newBrowseFieldsModal = ( props ) =>
379 new acf.models.browseFieldsModal( props );
380 } )( window.jQuery, undefined, window.acf );
381