-
Notifications
You must be signed in to change notification settings - Fork 101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Customizer context #399
base: main
Are you sure you want to change the base?
Changes from 9 commits
25f95bf
370d191
490f7a8
c5fc06e
be02e65
be00ddf
a79fe3e
c1c66d8
8f39b3f
980e2b5
0795c83
e017a20
bd39fe8
fbd2dbd
077968b
d6330b4
466ce6e
01893e7
2080bc1
96a9041
1155bf5
176460c
43b320a
3c13c56
8066242
591ae7b
1d6148c
5d51f6e
7c991b8
9e98884
9b2a851
ac92831
738d1ed
fa1c350
4005cf3
affc2ce
c9cb7e5
d71c9aa
75e5544
d4e0093
5484a07
8bb2f5d
24c04e4
31c7740
0b13e64
cdf92e0
d72e3dc
7065196
c24c240
45bff86
25c8fb0
82aaf30
163d44b
2396297
2571c9d
85f90e7
af2ed0f
42b1b5a
9d94adb
85f1efb
c8b87f7
e27ddda
a855da6
c6ec5b3
7cacfaa
59ad758
1ed2d45
2637ef2
e44f903
fb490c0
a34aee2
669eec6
f0be9ff
93b5061
de1ab07
451935b
9a94d30
804f8e1
38e6884
f154b9f
dfcefd7
1de94b3
afca523
32c61b6
11037a4
0053c3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* global document, jQuery, wp, _ */ | ||
/** | ||
* Integrate Fieldmanager and the Customizer. | ||
* | ||
* @param {function} $ jQuery | ||
* @param {function} api wp.customize API. | ||
* @param {function} _ Underscore | ||
*/ | ||
(function( $, api, _ ) { | ||
'use strict'; | ||
|
||
/** | ||
* Fires when an .fm-element input triggers a 'change' event. | ||
* | ||
* @param {Event} e Event object. | ||
*/ | ||
var onFmElementChange = function( e ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires when an .fm-element input triggers a 'keyup' event. | ||
* | ||
* @param {Event} e Event object. | ||
*/ | ||
var onFmElementKeyup = function( e ) { | ||
var $target = $( e.target ); | ||
|
||
// Ignore [Escape] and [Enter]. | ||
if ( 27 === e.keyCode || 13 === e.keyCode ) { | ||
return; | ||
} | ||
|
||
if ( $target.hasClass( 'fm-autocomplete' ) ) { | ||
// Update an autocomplete setting object when the input's text is deleted. | ||
if ( '' === $target.val() ) { | ||
// See fm.autocomplete.enable_autocomplete() for this tree. | ||
// @todo Risky? Autocomplete hidden fields don't typically get set to value="". | ||
$target.siblings( 'input[type=hidden]' ).first().val( '' ); | ||
|
||
/* | ||
* Don't update when typing into the autocomplete input. The hidden | ||
* field actually contains the value and is handled onFmElementChange(). | ||
*/ | ||
} else { | ||
return; | ||
} | ||
} | ||
|
||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires when a Fieldmanager object is dropped while sorting. | ||
* | ||
* @param {Event} e Event object. | ||
* @param {Element} el The sorted element. | ||
*/ | ||
var onFmSortableDrop = function( e, el ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires when Fieldmanager adds a new element in a repeatable field. | ||
* | ||
* @param {Event} e Event object. | ||
*/ | ||
var onFmAddedElement = function( e ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires when an item is selected and previewed in a Fieldmanager media field. | ||
* | ||
* @param {Event} e Event object. | ||
* @param {jQuery} $wrapper .media-wrapper jQuery object. | ||
* @param {object} attachment Attachment attributes. | ||
* @param {object} wp Global WordPress JS API. | ||
*/ | ||
var onFieldmanagerMediaPreview = function( e, $wrapper, attachment, wp ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires after clicking the "Remove" link of a Fieldmanager media field. | ||
* | ||
* @param {Event} e Event object. | ||
*/ | ||
var onFmMediaRemoveClick = function( e ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Fires after clicking the "Remove" link of a Fieldmanager repeatable field. | ||
* | ||
* @param {Event} e Event object. | ||
*/ | ||
var onFmjsRemoveClick = function( e ) { | ||
reserializeEachControl(); | ||
}; | ||
|
||
/** | ||
* Set the values of all Fieldmanager controls. | ||
*/ | ||
var reserializeEachControl = function() { | ||
api.control.each( reserializeControl ); | ||
}; | ||
|
||
/** | ||
* Set a Fieldmanager control to its form values. | ||
* | ||
* @param {Object} control Customizer Control object. | ||
*/ | ||
var reserializeControl = function( control ) { | ||
if ( 'fieldmanager' !== control.params.type ) { | ||
return; | ||
} | ||
|
||
control.setting.set( control.container.find( '.fm-element' ).serialize() ); | ||
}; | ||
|
||
/** | ||
* Trigger an event when a Customizer section with a Fieldmanager control expands. | ||
*/ | ||
var bindToSectionsWithFm = function() { | ||
// @todo Also do this on section add | ||
api.section.each(function( section ) { | ||
$.each( section.controls(), function( i, control ) { | ||
if ( 'fieldmanager' !== control.params.type ) { | ||
return; | ||
} | ||
|
||
section.container.bind( 'expanded', function() { | ||
$( document ).trigger( 'fm_customizer_control_section_expanded' ); | ||
}); | ||
|
||
return false; | ||
}); | ||
}); | ||
}; | ||
|
||
/** | ||
* Fires when the Customizer is loaded. | ||
*/ | ||
var ready = function() { | ||
var $document = $( document ); | ||
|
||
/* | ||
* We debounce() most keyup events to avoid refreshing the Customizer | ||
* preview every single time the user types a letter. But typing into | ||
* the autocomplete input does not itself trigger a refresh -- the only | ||
* time it should affect the preview is when removing an autocomplete | ||
* selection. We allow that to occur normally. | ||
*/ | ||
$document.on( 'keyup', '.fm-element:not(.fm-autocomplete)', _.debounce( onFmElementKeyup, 500 ) ); | ||
$document.on( 'keyup', '.fm-autocomplete', onFmElementKeyup ); | ||
|
||
$document.on( 'change', '.fm-element', onFmElementChange ); | ||
$document.on( 'click', '.fm-media-remove', onFmMediaRemoveClick ); | ||
$document.on( 'click', '.fmjs-remove', onFmjsRemoveClick ); | ||
$document.on( 'fm_sortable_drop', onFmSortableDrop ); | ||
$document.on( 'fieldmanager_media_preview', onFieldmanagerMediaPreview ); | ||
|
||
bindToSectionsWithFm(); | ||
|
||
/* | ||
* Hacky, because it always prompts the user to save. Unlike when we | ||
* create individual settings, the field "value" is always going to | ||
* change when it's reserialized. It also ensures defaults are applied | ||
* when the Customizer loads. But if the user saved those changes, the | ||
* defaults would be applied, as opposed to a submenu page, where there | ||
* isn't an AYS prompt. Creating a query string on the PHP side might | ||
* work, but that's even weirder. | ||
*/ | ||
reserializeEachControl(); | ||
}; | ||
|
||
if ( typeof api !== 'undefined' ) { | ||
api.bind( 'ready', ready ); | ||
} | ||
})( jQuery, wp.customize, _ ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,25 @@ var match_value = function( values, match_string ) { | |
return false; | ||
} | ||
|
||
/** | ||
* Initializes triggers to conditionally hide or show fields. | ||
*/ | ||
var init_display_if = function() { | ||
$( '.display-if' ).each(function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this could collide with another class in global scope. Can we narrow scope? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, although because this is existing FM code just relocated, I'd be inclined to determine a better scope in a separate PR. If that sounds OK to you, I'll file an issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sounds fine |
||
var src = $( this ).data( 'display-src' ); | ||
var values = $( this ).data( 'display-value' ).split( ',' ); | ||
var trigger = $( this ).siblings( '.fm-' + src + '-wrapper' ).find( '.fm-element' ); | ||
var val = trigger.val(); | ||
if ( trigger.is( ':radio' ) && trigger.filter( ':checked' ).length ) { | ||
val = trigger.filter( ':checked' ).val(); | ||
} | ||
trigger.addClass( 'display-trigger' ); | ||
if ( ! match_value( values, val ) ) { | ||
$( this ).hide(); | ||
} | ||
}); | ||
}; | ||
|
||
fm_add_another = function( $element ) { | ||
var el_name = $element.data( 'related-element' ) | ||
, limit = $element.data( 'limit' ) - 0 | ||
|
@@ -163,21 +182,6 @@ $( document ).ready( function () { | |
|
||
$( '.fm-collapsed > .fm-group:not(.fmjs-proto) > .fm-group-inner' ).hide(); | ||
|
||
// Initializes triggers to conditionally hide or show fields | ||
$( '.display-if' ).each( function() { | ||
var src = $( this ).data( 'display-src' ); | ||
var values = $( this ).data( 'display-value' ).split( ',' ); | ||
var trigger = $( this ).siblings( '.fm-' + src + '-wrapper' ).find( '.fm-element' ); | ||
var val = trigger.val(); | ||
if ( trigger.is( ':radio' ) && trigger.filter( ':checked' ).length ) { | ||
val = trigger.filter( ':checked' ).val(); | ||
} | ||
trigger.addClass( 'display-trigger' ); | ||
if ( !match_value( values, val ) ) { | ||
$( this ).hide(); | ||
} | ||
} ); | ||
|
||
// Controls the trigger to show or hide fields | ||
$( document ).on( 'change', '.display-trigger', function() { | ||
var val = $( this ).val().split(','); | ||
|
@@ -198,8 +202,11 @@ $( document ).ready( function () { | |
|
||
init_label_macros(); | ||
init_sortable(); | ||
init_display_if(); | ||
|
||
$( document ).on( 'fm_activate_tab', init_sortable ); | ||
$( document ).on( 'fm_activate_tab fm_customizer_control_section_expanded', init_sortable ); | ||
$( document ).on( 'fm_customizer_control_section_expanded', init_label_macros ); | ||
$( document ).on( 'fm_customizer_control_section_expanded', init_display_if ); | ||
} ); | ||
|
||
} )( jQuery ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
/** | ||
* Class file for Fieldmanager_Customize_Control. | ||
*/ | ||
|
||
if ( class_exists( 'WP_Customize_Control' ) ) : | ||
/** | ||
* Render a Fieldmanager field as a Customizer control. | ||
*/ | ||
class Fieldmanager_Customize_Control extends WP_Customize_Control { | ||
/** | ||
* The field object to use. Supply via `$args['fm']`. | ||
* | ||
* @var Fieldmanager_Field | ||
*/ | ||
protected $fm; | ||
|
||
/** | ||
* The control 'type', used in scripts to identify FM controls. | ||
* | ||
* @var string | ||
*/ | ||
public $type = 'fieldmanager'; | ||
|
||
/** | ||
* Enqueue control-related scripts and styles. | ||
*/ | ||
public function enqueue() { | ||
fm_add_script( | ||
'fm_customizer', | ||
'js/fieldmanager-customize.js', | ||
array( 'jquery', 'underscore', 'fieldmanager_script', 'customize-controls' ), | ||
'1.0.0', | ||
true | ||
); | ||
} | ||
|
||
/** | ||
* Render the control's content. | ||
* | ||
* @see Fieldmanager_Field::element_markup(). | ||
*/ | ||
public function render_content() { | ||
echo $this->fm->element_markup( $this->value() ); | ||
} | ||
} | ||
endif; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the prior to implementation approaches in addition to this approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. But the existing JS predates my involvement with FM, so I'd appreciate a review from you or someone else to confirm.