I’ve took my time to get in touch with jQuery, Backbone and the whole company and am pretty much satisfied by the job I’ve made on my multiple image theme customizer control; I’ve packed it into my libraries to increase its usability and the sometimes strange code is mainly due to that.
Queuing properly
Previous version of the control did print its CSS and JS components inline in the control markup. That’s easily removed using the WP_Customize_Control::enqueue
method
<?php
namespace tad\customizer\controls;
use tad\utils\Script as Script;
if (!class_exists('\WP_Customize_Control')) {
return null;
}
class MultiImageControl extends \WP_Customize_Control
{
public $type = 'multi-image';
protected $inputId = '';
protected $thumbnailsId = '';
public function __construct($manager, $id, $args = array())
{
parent::__construct($manager, $id, $args);
$this->inputId = $this->type . '-control-' . $this->id;
$this->thumbnailsId = $this->inputId . '-thumbnails';
}
public function enqueue()
{
wp_enqueue_media();
$jsPath = TADLIBS_ASSETS_URL . '/js/multi-image.js';
wp_enqueue_script('mutli-image-control', Script::suffix($jsPath), array('jquery'));
$cssPath = TADLIBS_ASSETS_URL . '/css/multi-image.css';
wp_enqueue_style('mutli-image-control', Script::suffix($cssPath));
}
...
Some interactivity
The basic version of the previous script did save the images selected in the Media Uploader but did not show any changes until the whole Theme Customizer page was refreshed: I’ve updated the script to allow some more interactivity
/*global jQuery, document, wp, console*/
jQuery(document).ready(function($) {
"use strict";
var file_frame, thumbnails;
// clicking the upload button will open the media frame
// and update the input field value
jQuery('a.multi-images-upload').each(function() {
var button = jQuery(this),
inputId = button.data('store');
// clicking the upload button will open the media manager
button.on('click', function(evt) {
evt.preventDefault();
// file frame already created, return
if (file_frame) {
file_frame.open();
return;
}
// create the file frame
file_frame = wp.media.frames.file_frame = wp.media({
title: jQuery(this).data('uploader_title'),
button: jQuery(this).data('uploader_button_text'),
multiple: true,
library: {
type: 'image'
}
});
// get the selected attachments when user confirms selection
file_frame.on('select', function(evt) {
var selected = file_frame.state().get('selection').toJSON(),
store = jQuery(inputId),
urls = [];
for (var i = selected.length - 1; i >= 0; i--) {
urls.push(selected[i].url);
}
// triggering change will activate the Save & Close button
store.val(urls).trigger('change');
// update the thumbnails using the new images
store.trigger('updateThumbnails', {
urls: urls
});
});
// open the file frame
file_frame.open();
});
});
// remove all images when the remove images button is pressed
jQuery('a.multi-images-remove').each(function() {
var button = $(this),
input = jQuery(button.data('store'));
button.on('click', function(evt) {
evt.preventDefault();
// update the hidden input value and save
input.val('').trigger('change');
// also refresh the thumbnails list
input.trigger('updateThumbnails', {
urls: ''
});
});
});
//update the images when the input value changes
jQuery('input.multi-images-control-input').each(function() {
var input = jQuery(this),
thumbContainer = jQuery(input.data('thumbs-container'));
input.on('updateThumbnails', function(evt, args) {
var urls = args.urls;
// remove old images
thumbContainer.empty();
// for each image url in the value create and append an image element to the list
for (var i = urls.length - 1; i >= 0; i--) {
var li = $('<li/>');
li.attr('style', 'background-image:url(' + urls[i] + ');');
li.attr('class', 'thumbnail');
li.attr('data-src', urls[i]);
thumbContainer.append(li);
}
});
});
});
I’ve used the data-something
attribute extensively in the code to avoid convolute function parameters and the two MultiImageControl
methods below will show
public function theButtons()
{
?>
<div>
<input type="hidden" value="<?php echo $this->value(); ?>" <?php $this->link(); ?> id="<?php echo $this->inputId; ?>" data-thumbs-container="#<?php echo $this->thumbnailsId; ?>" class="multi-images-control-input"/>
<a href="#" class="button-secondary multi-images-upload" data-store="#<?php echo $this->inputId; ?>">
<?php echo 'Upload'; ?>
</a>
<a href="#" class="button-secondary multi-images-remove" data-store="#<?php echo $this->inputId; ?>" data-thumbs-container="#<?php echo $this->thumbnailsId; ?>">
<?php echo 'Remove images'; ?>
</a>
</div>
<?php
}
public function theUploadedImages($srcs = array())
{
?>
<div class="customize-control-content">
<?php if (is_array($srcs)): ?>
<ul class="thumbnails" data-store="#<?php echo $this->inputId; ?>" id="<?php echo $this->thumbnailsId; ?>">
<?php foreach ($srcs as $src): ?>
<li class="thumbnail" style="background-image: url(<?php echo $src; ?>);" data-src="<?php echo $src; ?>">
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php
}
I will make the list sortable next and make more UI adjustments to make the control, that might be used by uncaring users, as easy to use as possible.
Code on GitHub
Actual code is available online.