PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.4.1-beta7
Secure Custom Fields v6.4.1-beta7
6.9.5 6.9.4 6.9.3 6.9.2 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 / _acf-field-icon-picker.js
secure-custom-fields / assets / src / js Last commit date
pro 1 year ago _acf-compatibility.js 1 year ago _acf-condition-types.js 1 year ago _acf-condition.js 1 year ago _acf-conditions.js 1 year ago _acf-field-accordion.js 1 year ago _acf-field-button-group.js 1 year ago _acf-field-checkbox.js 1 year ago _acf-field-color-picker.js 1 year ago _acf-field-date-picker.js 1 year ago _acf-field-date-time-picker.js 1 year ago _acf-field-file.js 1 year ago _acf-field-google-map.js 1 year ago _acf-field-icon-picker.js 1 year ago _acf-field-image.js 1 year ago _acf-field-link.js 1 year ago _acf-field-oembed.js 1 year ago _acf-field-page-link.js 1 year ago _acf-field-post-object.js 1 year ago _acf-field-radio.js 1 year ago _acf-field-range.js 1 year ago _acf-field-relationship.js 1 year ago _acf-field-select.js 1 year ago _acf-field-tab.js 1 year ago _acf-field-taxonomy.js 1 year ago _acf-field-time-picker.js 1 year ago _acf-field-true-false.js 1 year ago _acf-field-url.js 1 year ago _acf-field-user.js 1 year ago _acf-field-wysiwyg.js 1 year 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 1 year ago _acf-media.js 1 year ago _acf-modal.js 1 year ago _acf-model.js 1 year ago _acf-notice.js 1 year ago _acf-panel.js 1 year ago _acf-popup.js 1 year ago _acf-postbox.js 1 year ago _acf-screen.js 1 year ago _acf-select2.js 1 year ago _acf-tinymce.js 1 year ago _acf-tooltip.js 1 year ago _acf-unload.js 1 year ago _acf-validation.js 1 year ago _acf.js 1 year ago _browse-fields-modal.js 1 year ago _field-group-compatibility.js 1 year ago _field-group-conditions.js 1 year ago _field-group-field.js 1 year ago _field-group-fields.js 1 year ago _field-group-locations.js 1 year ago _field-group-settings.js 1 year ago _field-group.js 1 year 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
_acf-field-icon-picker.js
420 lines
1 ( function ( $, undefined ) {
2 const Field = acf.Field.extend( {
3 type: 'icon_picker',
4
5 wait: 'load',
6
7 events: {
8 showField: 'scrollToSelectedDashicon',
9 'input .acf-icon_url': 'onUrlChange',
10 'click .acf-icon-picker-dashicon': 'onDashiconClick',
11 'focus .acf-icon-picker-dashicon-radio': 'onDashiconRadioFocus',
12 'blur .acf-icon-picker-dashicon-radio': 'onDashiconRadioBlur',
13 'keydown .acf-icon-picker-dashicon-radio': 'onDashiconKeyDown',
14 'input .acf-dashicons-search-input': 'onDashiconSearch',
15 'keydown .acf-dashicons-search-input': 'onDashiconSearchKeyDown',
16 'click .acf-icon-picker-media-library-button':
17 'onMediaLibraryButtonClick',
18 'click .acf-icon-picker-media-library-preview':
19 'onMediaLibraryButtonClick',
20 },
21
22 $typeInput() {
23 return this.$(
24 'input[type="hidden"][data-hidden-type="type"]:first'
25 );
26 },
27
28 $valueInput() {
29 return this.$(
30 'input[type="hidden"][data-hidden-type="value"]:first'
31 );
32 },
33
34 $tabButton() {
35 return this.$( '.acf-tab-button' );
36 },
37
38 $selectedIcon() {
39 return this.$( '.acf-icon-picker-dashicon.active' );
40 },
41
42 $selectedRadio() {
43 return this.$( '.acf-icon-picker-dashicon.active input' );
44 },
45
46 $dashiconsList() {
47 return this.$( '.acf-dashicons-list' );
48 },
49
50 $mediaLibraryButton() {
51 return this.$( '.acf-icon-picker-media-library-button' );
52 },
53
54 initialize() {
55 // Set up actions hook callbacks.
56 this.addActions();
57
58 // Initialize the state of the icon picker.
59 let typeAndValue = {
60 type: this.$typeInput().val(),
61 value: this.$valueInput().val()
62 };
63
64 // Store the type and value object.
65 this.set( 'typeAndValue', typeAndValue );
66
67 // Any time any acf tab is clicked, we will re-scroll to the selected dashicon.
68 $( '.acf-tab-button' ).on( 'click', () => {
69 this.initializeDashiconsTab( this.get( 'typeAndValue' ) );
70 } );
71
72 // Fire the action which lets people know the state has been updated.
73 acf.doAction(
74 this.get( 'name' ) + '/type_and_value_change',
75 typeAndValue
76 );
77
78 this.initializeDashiconsTab( typeAndValue );
79 this.alignMediaLibraryTabToCurrentValue( typeAndValue );
80 },
81
82 addActions() {
83 // Set up an action listener for when the type and value changes.
84 acf.addAction(
85 this.get( 'name' ) + '/type_and_value_change',
86 ( newTypeAndValue ) => {
87 // Align the visual state of each tab to the current value.
88 this.alignDashiconsTabToCurrentValue( newTypeAndValue );
89 this.alignMediaLibraryTabToCurrentValue( newTypeAndValue );
90 this.alignUrlTabToCurrentValue( newTypeAndValue );
91 }
92 );
93 },
94
95 updateTypeAndValue( type, value ) {
96 const typeAndValue = {
97 type,
98 value,
99 };
100
101 // Update the values in the hidden fields, which are what will actually be saved.
102 acf.val( this.$typeInput(), type );
103 acf.val( this.$valueInput(), value );
104
105 // Fire an action to let each tab set itself according to the typeAndValue state.
106 acf.doAction(
107 this.get( 'name' ) + '/type_and_value_change',
108 typeAndValue
109 );
110
111 // Set the state.
112 this.set( 'typeAndValue', typeAndValue );
113 },
114
115 scrollToSelectedDashicon() {
116 const innerElement = this.$selectedIcon();
117
118 // If no icon is selected, do nothing.
119 if ( innerElement.length === 0 ) {
120 return;
121 }
122
123 const scrollingDiv = this.$dashiconsList();
124 scrollingDiv.scrollTop( 0 );
125
126 const distance = innerElement.position().top - 50;
127
128 if ( distance === 0 ) {
129 return;
130 }
131
132 scrollingDiv.scrollTop( distance );
133 },
134
135 initializeDashiconsTab( typeAndValue ) {
136 const dashicons = this.getDashiconsList() || [];
137 this.set( 'dashicons', dashicons );
138 this.renderDashiconList();
139 this.initializeSelectedDashicon( typeAndValue );
140 },
141
142 initializeSelectedDashicon( typeAndValue ) {
143 if ( typeAndValue.type !== 'dashicons' ) {
144 return;
145 }
146 // Select the correct dashicon.
147 this.selectDashicon( typeAndValue.value, false ).then( () => {
148 // Scroll to the selected dashicon.
149 this.scrollToSelectedDashicon();
150 } );
151 },
152
153 alignDashiconsTabToCurrentValue( typeAndValue ) {
154 if ( typeAndValue.type !== 'dashicons' ) {
155 this.unselectDashicon();
156 }
157 },
158
159 renderDashiconHTML( dashicon ) {
160 const id = `${ this.get( 'name' ) }-${ dashicon.key }`;
161 return `<div class="dashicons ${ acf.strEscape(
162 dashicon.key
163 ) } acf-icon-picker-dashicon" data-icon="${ acf.strEscape(
164 dashicon.key
165 ) }">
166 <label for="${ acf.strEscape( id ) }">${ acf.strEscape(
167 dashicon.label
168 ) }</label>
169 <input id="${ acf.strEscape(
170 id
171 ) }" type="radio" class="acf-icon-picker-dashicon-radio" name="acf-icon-picker-dashicon-radio" value="${ acf.strEscape(
172 dashicon.key
173 ) }">
174 </div>`;
175 },
176
177 renderDashiconList() {
178 const dashicons = this.get( 'dashicons' );
179
180 this.$dashiconsList().empty();
181 dashicons.forEach( ( dashicon ) => {
182 this.$dashiconsList().append(
183 this.renderDashiconHTML( dashicon )
184 );
185 } );
186 },
187
188 getDashiconsList() {
189 const iconPickeri10n = acf.get( 'iconPickeri10n' ) || [];
190
191 const dashicons = Object.entries( iconPickeri10n ).map(
192 ( [ key, value ] ) => {
193 return {
194 key,
195 label: value,
196 };
197 }
198 );
199
200 return dashicons;
201 },
202
203 getDashiconsBySearch( searchTerm ) {
204 const lowercaseSearchTerm = searchTerm.toLowerCase();
205 const dashicons = this.getDashiconsList();
206
207 const filteredDashicons = dashicons.filter( function ( icon ) {
208 const lowercaseIconLabel = icon.label.toLowerCase();
209 return lowercaseIconLabel.indexOf( lowercaseSearchTerm ) > -1;
210 } );
211
212 return filteredDashicons;
213 },
214
215 selectDashicon( dashicon, setFocus = true ) {
216 this.set( 'selectedDashicon', dashicon );
217
218 // Select the new one.
219 const $newIcon = this.$dashiconsList().find(
220 '.acf-icon-picker-dashicon[data-icon="' + dashicon + '"]'
221 );
222 $newIcon.addClass( 'active' );
223
224 const $input = $newIcon.find( 'input' );
225 const thePromise = $input.prop( 'checked', true ).promise();
226
227 if ( setFocus ) {
228 $input.trigger( 'focus' );
229 }
230
231 this.updateTypeAndValue( 'dashicons', dashicon );
232
233 return thePromise;
234 },
235
236 unselectDashicon() {
237 // Remove the currently active dashicon, if any.
238 this.$dashiconsList()
239 .find( '.acf-icon-picker-dashicon' )
240 .removeClass( 'active' );
241 this.set( 'selectedDashicon', false );
242 },
243
244 onDashiconRadioFocus( e ) {
245 const dashicon = e.target.value;
246
247 const $newIcon = this.$dashiconsList().find(
248 '.acf-icon-picker-dashicon[data-icon="' + dashicon + '"]'
249 );
250 $newIcon.addClass( 'focus' );
251
252 // If this is a different icon than previously selected, select it.
253 if ( this.get( 'selectedDashicon' ) !== dashicon ) {
254 this.unselectDashicon();
255 this.selectDashicon( dashicon );
256 }
257 },
258
259 onDashiconRadioBlur( e ) {
260 const icon = this.$( e.target );
261 const iconParent = icon.parent();
262
263 iconParent.removeClass( 'focus' );
264 },
265
266 onDashiconClick( e ) {
267 e.preventDefault();
268
269 const icon = this.$( e.target );
270 const dashicon = icon.find( 'input' ).val();
271
272 const $newIcon = this.$dashiconsList().find(
273 '.acf-icon-picker-dashicon[data-icon="' + dashicon + '"]'
274 );
275
276 // By forcing focus on the input, we fire onDashiconRadioFocus.
277 $newIcon.find( 'input' ).prop( 'checked', true ).trigger( 'focus' );
278 },
279
280 onDashiconSearch( e ) {
281 const searchTerm = e.target.value;
282 const filteredDashicons = this.getDashiconsBySearch( searchTerm );
283
284 if ( filteredDashicons.length > 0 || ! searchTerm ) {
285 this.set( 'dashicons', filteredDashicons );
286 this.$( '.acf-dashicons-list-empty' ).hide();
287 this.$( '.acf-dashicons-list ' ).show();
288 this.renderDashiconList();
289
290 // Announce change of data to screen readers.
291 wp.a11y.speak(
292 acf.get( 'iconPickerA11yStrings' )
293 .newResultsFoundForSearchTerm,
294 'polite'
295 );
296 } else {
297 // Truncate the search term if it's too long.
298 const visualSearchTerm =
299 searchTerm.length > 30
300 ? searchTerm.substring( 0, 30 ) + '&hellip;'
301 : searchTerm;
302
303 this.$( '.acf-dashicons-list ' ).hide();
304 this.$( '.acf-dashicons-list-empty' )
305 .find( '.acf-invalid-dashicon-search-term' )
306 .text( visualSearchTerm );
307 this.$( '.acf-dashicons-list-empty' ).css( 'display', 'flex' );
308 this.$( '.acf-dashicons-list-empty' ).show();
309
310 // Announce change of data to screen readers.
311 wp.a11y.speak(
312 acf.get( 'iconPickerA11yStrings' ).noResultsForSearchTerm,
313 'polite'
314 );
315 }
316 },
317
318 onDashiconSearchKeyDown( e ) {
319 // Check if the pressed key is Enter (key code 13)
320 if ( e.which === 13 ) {
321 // Prevent submitting the entire form if someone presses enter after searching.
322 e.preventDefault();
323 }
324 },
325
326 onDashiconKeyDown( e ) {
327 if ( e.which === 13 ) {
328 // If someone presses enter while an icon is focused, prevent the form from submitting.
329 e.preventDefault();
330 }
331 },
332
333 alignMediaLibraryTabToCurrentValue( typeAndValue ) {
334 const type = typeAndValue.type;
335 const value = typeAndValue.value;
336
337 if ( type !== 'media_library' && type !== 'dashicons' ) {
338 // Hide the preview container on the media library tab.
339 this.$( '.acf-icon-picker-media-library-preview' ).hide();
340 }
341
342 if ( type === 'media_library' ) {
343 const previewUrl = this.get( 'mediaLibraryPreviewUrl' );
344 // Set the image file preview src.
345 this.$( '.acf-icon-picker-media-library-preview-img img' ).attr(
346 'src',
347 previewUrl
348 );
349
350 // Hide the dashicon preview.
351 this.$(
352 '.acf-icon-picker-media-library-preview-dashicon'
353 ).hide();
354
355 // Show the image file preview.
356 this.$( '.acf-icon-picker-media-library-preview-img' ).show();
357
358 // Show the preview container (it may have been hidden if nothing was ever selected yet).
359 this.$( '.acf-icon-picker-media-library-preview' ).show();
360 }
361
362 if ( type === 'dashicons' ) {
363 // Set the dashicon preview class.
364 this.$(
365 '.acf-icon-picker-media-library-preview-dashicon .dashicons'
366 ).attr( 'class', 'dashicons ' + value );
367
368 // Hide the image file preview.
369 this.$( '.acf-icon-picker-media-library-preview-img' ).hide();
370
371 // Show the dashicon preview.
372 this.$(
373 '.acf-icon-picker-media-library-preview-dashicon'
374 ).show();
375
376 // Show the preview container (it may have been hidden if nothing was ever selected yet).
377 this.$( '.acf-icon-picker-media-library-preview' ).show();
378 }
379 },
380
381 async onMediaLibraryButtonClick( e ) {
382 e.preventDefault();
383
384 await this.selectAndReturnAttachment().then( ( attachment ) => {
385 // When an attachment is selected, update the preview and the hidden fields.
386 this.set( 'mediaLibraryPreviewUrl', attachment.attributes.url );
387 this.updateTypeAndValue( 'media_library', attachment.id );
388 } );
389 },
390
391 selectAndReturnAttachment() {
392 return new Promise( ( resolve ) => {
393 acf.newMediaPopup( {
394 mode: 'select',
395 type: 'image',
396 title: acf.__( 'Select Image' ),
397 field: this.get( 'key' ),
398 multiple: false,
399 library: 'all',
400 allowedTypes: 'image',
401 select: resolve,
402 } );
403 } );
404 },
405
406 alignUrlTabToCurrentValue( typeAndValue ) {
407 if ( typeAndValue.type !== 'url' ) {
408 this.$( '.acf-icon_url' ).val( '' );
409 }
410 },
411
412 onUrlChange( event ) {
413 const currentValue = event.target.value;
414 this.updateTypeAndValue( 'url', currentValue );
415 },
416 } );
417
418 acf.registerFieldType( Field );
419 } )( jQuery );
420