Skip to content

Commit a15ed1a

Browse files
authored
Enhancement: Add new acf/fields/icon_picker/{tab_name}/icons filter (#177)
* Move icon list code into new render_icon_list_tab method * Add data-tab attribute * Add new `acf/fields/icon_picker/{tab_name}/icons` filter * Update class names * Rename $dashiconsList to $iconsList, update selector * Rename event handlers * Start renaming methods * Update initializeIconLists * Remove now-obsolete initializeSelectedDashicon method * Update getIconsList * Less weird * Update renderIconList * Rename renderDashiconHTML to renderIconHTML * Update renderIconHTML * Update selectIcon * Update unselectIcon * Update onIconRadioFocus * Update onIconClick * Remove stray parens * Pass element to unselectIcon * Fix search related methods * Correctly set search results * Only set style attr if url is set * Update alignIconListTabsToCurrentValue * Add mising const * Update scrollToSelectedIcon * Various fixes * Minor comment wording tweaks * Remove stray second function argument
1 parent e984f94 commit a15ed1a

File tree

4 files changed

+236
-177
lines changed

4 files changed

+236
-177
lines changed

assets/src/js/_acf-field-icon-picker.js

Lines changed: 139 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
wait: 'load',
66

77
events: {
8-
showField: 'scrollToSelectedDashicon',
8+
showField: 'scrollToSelectedIcon',
99
'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',
10+
'click .acf-icon-picker-list-icon': 'onIconClick',
11+
'focus .acf-icon-picker-list-icon-radio': 'onIconRadioFocus',
12+
'blur .acf-icon-picker-list-icon-radio': 'onIconRadioBlur',
13+
'keydown .acf-icon-picker-list-icon-radio': 'onIconKeyDown',
14+
'input .acf-icon-list-search-input': 'onIconSearch',
15+
'keydown .acf-icon-list-search-input': 'onIconSearchKeyDown',
1616
'click .acf-icon-picker-media-library-button':
1717
'onMediaLibraryButtonClick',
1818
'click .acf-icon-picker-media-library-preview':
@@ -36,15 +36,15 @@
3636
},
3737

3838
$selectedIcon() {
39-
return this.$( '.acf-icon-picker-dashicon.active' );
39+
return this.$( '.acf-icon-picker-list-icon.active' );
4040
},
4141

4242
$selectedRadio() {
43-
return this.$( '.acf-icon-picker-dashicon.active input' );
43+
return this.$( '.acf-icon-picker-list-icon.active input' );
4444
},
4545

46-
$dashiconsList() {
47-
return this.$( '.acf-dashicons-list' );
46+
$iconsList() {
47+
return this.$( '.acf-icon-list:visible' );
4848
},
4949

5050
$mediaLibraryButton() {
@@ -64,9 +64,9 @@
6464
// Store the type and value object.
6565
this.set( 'typeAndValue', typeAndValue );
6666

67-
// Any time any acf tab is clicked, we will re-scroll to the selected dashicon.
67+
// Any time any acf tab is clicked, we will re-scroll to the selected icon.
6868
$( '.acf-tab-button' ).on( 'click', () => {
69-
this.initializeDashiconsTab( this.get( 'typeAndValue' ) );
69+
this.initializeIconLists( this.get( 'typeAndValue' ) );
7070
} );
7171

7272
// Fire the action which lets people know the state has been updated.
@@ -75,7 +75,7 @@
7575
typeAndValue
7676
);
7777

78-
this.initializeDashiconsTab( typeAndValue );
78+
this.initializeIconLists( typeAndValue );
7979
this.alignMediaLibraryTabToCurrentValue( typeAndValue );
8080
},
8181

@@ -85,7 +85,7 @@
8585
this.get( 'name' ) + '/type_and_value_change',
8686
( newTypeAndValue ) => {
8787
// Align the visual state of each tab to the current value.
88-
this.alignDashiconsTabToCurrentValue( newTypeAndValue );
88+
this.alignIconListTabsToCurrentValue( newTypeAndValue );
8989
this.alignMediaLibraryTabToCurrentValue( newTypeAndValue );
9090
this.alignUrlTabToCurrentValue( newTypeAndValue );
9191
}
@@ -112,15 +112,15 @@
112112
this.set( 'typeAndValue', typeAndValue );
113113
},
114114

115-
scrollToSelectedDashicon() {
115+
scrollToSelectedIcon() {
116116
const innerElement = this.$selectedIcon();
117117

118118
// If no icon is selected, do nothing.
119119
if ( innerElement.length === 0 ) {
120120
return;
121121
}
122122

123-
const scrollingDiv = this.$dashiconsList();
123+
const scrollingDiv = innerElement.closest( '.acf-icon-list' );
124124
scrollingDiv.scrollTop( 0 );
125125

126126
const distance = innerElement.position().top - 50;
@@ -132,92 +132,115 @@
132132
scrollingDiv.scrollTop( distance );
133133
},
134134

135-
initializeDashiconsTab( typeAndValue ) {
136-
const dashicons = this.getDashiconsList() || [];
137-
this.set( 'dashicons', dashicons );
138-
this.renderDashiconList();
139-
this.initializeSelectedDashicon( typeAndValue );
135+
initializeIconLists( typeAndValue ) {
136+
const self = this;
137+
138+
this.$( '.acf-icon-list' ).each( function( i ) {
139+
const tabName = $( this ).data( 'parent-tab' );
140+
const icons = self.getIconsList( tabName ) || [];
141+
self.set( tabName, icons );
142+
self.renderIconList( $( this ) );
143+
144+
if ( typeAndValue.type === tabName ) {
145+
// Select the correct icon.
146+
self.selectIcon( $( this ), typeAndValue.value, false ).then( () => {
147+
// Scroll to the selected icon.
148+
self.scrollToSelectedIcon();
149+
} );
150+
}
151+
} );
140152
},
141153

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();
154+
alignIconListTabsToCurrentValue( typeAndValue ) {
155+
const icons = this.$( '.acf-icon-list' ).filter(
156+
function () {
157+
return (
158+
$( this ).data( 'parent-tab' ) !== typeAndValue.type
159+
);
160+
}
161+
);
162+
const self = this;
163+
icons.each( function () {
164+
self.unselectIcon( $( this ) );
150165
} );
151166
},
152167

153-
alignDashiconsTabToCurrentValue( typeAndValue ) {
154-
if ( typeAndValue.type !== 'dashicons' ) {
155-
this.unselectDashicon();
168+
renderIconHTML( tabName, icon ) {
169+
const id = `${ this.get( 'name' ) }-${ icon.key }`;
170+
171+
let style = '';
172+
if ( 'dashicons' !== tabName ) {
173+
style = `background: center / contain url( ${ acf.strEscape(
174+
icon.url
175+
) } ) no-repeat;`;
156176
}
157-
},
158177

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
178+
return `<div class="${ tabName } ${ acf.strEscape(
179+
icon.key
180+
) } acf-icon-picker-list-icon" role="radio" data-icon="${ acf.strEscape(
181+
icon.key
182+
) }" style="${ style }" title="${ acf.strEscape(
183+
icon.label
165184
) }">
166185
<label for="${ acf.strEscape( id ) }">${ acf.strEscape(
167-
dashicon.label
186+
icon.label
168187
) }</label>
169188
<input id="${ acf.strEscape(
170189
id
171-
) }" type="radio" class="acf-icon-picker-dashicon-radio" name="acf-icon-picker-dashicon-radio" value="${ acf.strEscape(
172-
dashicon.key
190+
) }" type="radio" class="acf-icon-picker-list-icon-radio" name="acf-icon-picker-list-icon-radio" value="${ acf.strEscape(
191+
icon.key
173192
) }">
174193
</div>`;
175194
},
176195

177-
renderDashiconList() {
178-
const dashicons = this.get( 'dashicons' );
196+
renderIconList( $el ) {
197+
const tabName = $el.data( 'parent-tab' );
198+
const icons = this.get( tabName );
179199

180-
this.$dashiconsList().empty();
181-
dashicons.forEach( ( dashicon ) => {
182-
this.$dashiconsList().append(
183-
this.renderDashiconHTML( dashicon )
184-
);
185-
} );
200+
$el.empty();
201+
if ( icons ) {
202+
icons.forEach( ( icon ) => {
203+
const iconHTML = this.renderIconHTML( tabName, icon );
204+
$el.append( iconHTML );
205+
} );
206+
}
186207
},
187208

188-
getDashiconsList() {
189-
const iconPickeri10n = acf.get( 'iconPickeri10n' ) || [];
209+
getIconsList( tabName ) {
210+
if ( 'dashicons' === tabName ) {
211+
const iconPickeri10n = acf.get( 'iconPickeri10n' ) || [];
190212

191-
const dashicons = Object.entries( iconPickeri10n ).map(
192-
( [ key, value ] ) => {
193-
return {
194-
key,
195-
label: value,
196-
};
197-
}
198-
);
213+
return Object.entries( iconPickeri10n ).map(
214+
( [ key, value ] ) => {
215+
return {
216+
key,
217+
label: value,
218+
};
219+
}
220+
);
221+
}
199222

200-
return dashicons;
223+
return acf.get( `iconPickerIcons_${ tabName }` );
201224
},
202225

203-
getDashiconsBySearch( searchTerm ) {
226+
getIconsBySearch( searchTerm, tabName ) {
204227
const lowercaseSearchTerm = searchTerm.toLowerCase();
205-
const dashicons = this.getDashiconsList();
228+
const icons = this.getIconsList( tabName);
206229

207-
const filteredDashicons = dashicons.filter( function ( icon ) {
230+
const filteredIcons = icons.filter( function ( icon ) {
208231
const lowercaseIconLabel = icon.label.toLowerCase();
209232
return lowercaseIconLabel.indexOf( lowercaseSearchTerm ) > -1;
210233
} );
211234

212-
return filteredDashicons;
235+
return filteredIcons;
213236
},
214237

215-
selectDashicon( dashicon, setFocus = true ) {
216-
this.set( 'selectedDashicon', dashicon );
238+
selectIcon( $el, icon, setFocus = true ) {
239+
this.set( 'selectedIcon', icon );
217240

218241
// Select the new one.
219-
const $newIcon = this.$dashiconsList().find(
220-
'.acf-icon-picker-dashicon[data-icon="' + dashicon + '"]'
242+
const $newIcon = $el.find(
243+
'.acf-icon-picker-list-icon[data-icon="' + icon + '"]'
221244
);
222245
$newIcon.addClass( 'active' );
223246

@@ -228,64 +251,75 @@
228251
$input.trigger( 'focus' );
229252
}
230253

231-
this.updateTypeAndValue( 'dashicons', dashicon );
254+
this.updateTypeAndValue( $el.data( 'parent-tab' ), icon );
232255

233256
return thePromise;
234257
},
235258

236-
unselectDashicon() {
259+
unselectIcon( $el ) {
237260
// Remove the currently active dashicon, if any.
238-
this.$dashiconsList()
239-
.find( '.acf-icon-picker-dashicon' )
261+
$el
262+
.find( '.acf-icon-picker-list-icon' )
240263
.removeClass( 'active' );
241-
this.set( 'selectedDashicon', false );
264+
this.set( 'selectedIcon', false );
242265
},
243266

244-
onDashiconRadioFocus( e ) {
245-
const dashicon = e.target.value;
267+
onIconRadioFocus( e ) {
268+
const icon = e.target.value;
269+
const $tabs = this.$( e.target ).closest(
270+
'.acf-icon-picker-tabs'
271+
);
272+
const $iconsList = $tabs.find( '.acf-icon-list' );
246273

247-
const $newIcon = this.$dashiconsList().find(
248-
'.acf-icon-picker-dashicon[data-icon="' + dashicon + '"]'
274+
const $newIcon = $iconsList.find(
275+
'.acf-icon-picker-list-icon[data-icon="' + icon + '"]'
249276
);
250277
$newIcon.addClass( 'focus' );
251278

252279
// If this is a different icon than previously selected, select it.
253-
if ( this.get( 'selectedDashicon' ) !== dashicon ) {
254-
this.unselectDashicon();
255-
this.selectDashicon( dashicon );
280+
if ( this.get( 'selectedIcon' ) !== icon ) {
281+
this.unselectIcon( $iconsList );
282+
this.selectIcon( $iconsList, icon );
256283
}
257284
},
258285

259-
onDashiconRadioBlur( e ) {
286+
onIconRadioBlur( e ) {
260287
const icon = this.$( e.target );
261288
const iconParent = icon.parent();
262289

263290
iconParent.removeClass( 'focus' );
264291
},
265292

266-
onDashiconClick( e ) {
293+
onIconClick( e ) {
267294
e.preventDefault();
295+
const $iconList = this.$( e.target ).closest(
296+
'.acf-icon-list'
297+
);
298+
const $iconElement = this.$( e.target );
299+
const icon = $iconElement.find( 'input' ).val();
268300

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 + '"]'
301+
const $newIconElement = $iconList.find(
302+
'.acf-icon-picker-list-icon[data-icon="' + icon + '"]'
274303
);
275304

276-
// By forcing focus on the input, we fire onDashiconRadioFocus.
277-
$newIcon.find( 'input' ).prop( 'checked', true ).trigger( 'focus' );
305+
// By forcing focus on the input, we fire onIconRadioFocus.
306+
$newIconElement.find( 'input' ).prop( 'checked', true ).trigger( 'focus' );
278307
},
279308

280-
onDashiconSearch( e ) {
309+
onIconSearch( e ) {
310+
const $tabs = this.$( e.target ).closest(
311+
'.acf-icon-picker-tabs'
312+
);
313+
const $iconList = $tabs.find( '.acf-icon-list' );
314+
const tabName = $tabs.data( 'tab' );
281315
const searchTerm = e.target.value;
282-
const filteredDashicons = this.getDashiconsBySearch( searchTerm );
316+
const filteredIcons = this.getIconsBySearch( searchTerm, tabName );
283317

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();
318+
if ( filteredIcons.length > 0 || ! searchTerm ) {
319+
this.set( tabName, filteredIcons );
320+
$tabs.find( '.acf-icon-list-empty' ).hide();
321+
$tabs.find( '.acf-icon-list ' ).show();
322+
this.renderIconList( $iconList );
289323

290324
// Announce change of data to screen readers.
291325
wp.a11y.speak(
@@ -300,12 +334,12 @@
300334
? searchTerm.substring( 0, 30 ) + '&hellip;'
301335
: searchTerm;
302336

303-
this.$( '.acf-dashicons-list ' ).hide();
304-
this.$( '.acf-dashicons-list-empty' )
305-
.find( '.acf-invalid-dashicon-search-term' )
337+
$tabs.find( '.acf-icon-list ' ).hide();
338+
$tabs.find( '.acf-icon-list-empty' )
339+
.find( '.acf-invalid-icon-list-search-term' )
306340
.text( visualSearchTerm );
307-
this.$( '.acf-dashicons-list-empty' ).css( 'display', 'flex' );
308-
this.$( '.acf-dashicons-list-empty' ).show();
341+
$tabs.find( '.acf-icon-list-empty' ).css( 'display', 'flex' );
342+
$tabs.find( '.acf-icon-list-empty' ).show();
309343

310344
// Announce change of data to screen readers.
311345
wp.a11y.speak(
@@ -315,15 +349,15 @@
315349
}
316350
},
317351

318-
onDashiconSearchKeyDown( e ) {
352+
onIconSearchKeyDown( e ) {
319353
// Check if the pressed key is Enter (key code 13)
320354
if ( e.which === 13 ) {
321355
// Prevent submitting the entire form if someone presses enter after searching.
322356
e.preventDefault();
323357
}
324358
},
325359

326-
onDashiconKeyDown( e ) {
360+
onIconKeyDown( e ) {
327361
if ( e.which === 13 ) {
328362
// If someone presses enter while an icon is focused, prevent the form from submitting.
329363
e.preventDefault();

0 commit comments

Comments
 (0)