diff --git a/js/customize-dynamic-control.js b/js/customize-dynamic-control.js
index 7942523..f186339 100644
--- a/js/customize-dynamic-control.js
+++ b/js/customize-dynamic-control.js
@@ -131,10 +131,6 @@
control._setUpSettingPropertyLinks();
api.Control.prototype.ready.call( control );
-
- // @todo build out the controls for the post when Control is expanded.
- // @todo Let the Control title include the post title.
- control.deferred.embedded.done(function() {});
},
/**
@@ -182,6 +178,28 @@
control.deferred.embedded.resolve(); // This triggers control.ready().
},
+ /**
+ * Render the control from its JS template, if it exists.
+ *
+ * @returns {void}
+ */
+ renderContent: function renderContent() {
+ var control = this, template;
+
+ if ( control.params.content_template ) {
+ if ( 'function' === typeof control.params.content_template ) {
+ template = control.params.content_template;
+ } else {
+ template = wp.template( control.params.content_template );
+ }
+ if ( control.container ) {
+ control.container.html( template( control.params ) );
+ }
+ } else {
+ api.Control.prototype.renderContent.call( control );
+ }
+ },
+
/**
* This is not working with autofocus.
*
diff --git a/js/customize-post-section.js b/js/customize-post-section.js
index a933b6b..77cafa7 100644
--- a/js/customize-post-section.js
+++ b/js/customize-post-section.js
@@ -275,6 +275,9 @@
if ( postTypeObj.supports.editor ) {
section.addContentControl();
}
+ if ( 'undefined' === typeof EditPostPreviewCustomize && api.Widgets && api.Posts.data.themeSupportsWidgets ) {
+ section.addPostWidgetAreasControl();
+ }
if ( postTypeObj.supports.excerpt ) {
section.addExcerptControl();
}
@@ -520,6 +523,7 @@
control = new api.controlConstructor.post_editor( section.id + '[post_content]', {
params: {
section: section.id,
+ priority: 25,
label: postTypeObj.labels.content_field ? postTypeObj.labels.content_field : api.Posts.data.l10n.fieldContentLabel,
setting_property: 'post_content',
settings: {
@@ -544,6 +548,35 @@
return control;
},
+ /**
+ * Add widget area shortcuts control.
+ *
+ * @returns {wp.customize.Control} Control
+ */
+ addPostWidgetAreasControl: function() {
+ var section = this, control;
+
+ control = new api.controlConstructor.sidebar_shortcuts( section.id + '[sidebar_shortcuts]', {
+ params: {
+ section: section.id,
+ priority: 26, // After content.
+ label: api.Posts.data.l10n.fieldWidgetAreasLabel,
+ settings: []
+ }
+ } );
+
+ // Override preview trying to de-activate control not present in preview context. See WP Trac #37270.
+ control.active.validate = function() {
+ return true;
+ };
+
+ // Register.
+ section.postFieldControls.sidebar_shortcuts = control;
+ api.control.add( control.id, control );
+
+ return control;
+ },
+
/**
* Add post excerpt control.
*
@@ -594,7 +627,7 @@
params: {
section: section.id,
priority: 60,
- label: postTypeObj.labels.discussion_field ? postTypeObj.labels.discussion_field : api.Posts.data.l10n.fieldDiscusionLabel,
+ label: postTypeObj.labels.discussion_field ? postTypeObj.labels.discussion_field : api.Posts.data.l10n.fieldDiscussionLabel,
active: true,
settings: {
'default': setting.id
diff --git a/js/customize-posts.js b/js/customize-posts.js
index 78fcf98..4381b6b 100644
--- a/js/customize-posts.js
+++ b/js/customize-posts.js
@@ -15,6 +15,7 @@
component.data = {
postTypes: {},
+ themeSupportsWidgets: true,
initialServerDate: '',
initialServerTimestamp: 0,
initialClientTimestamp: ( new Date() ).valueOf(),
diff --git a/js/customize-sidebar-shortcuts-control.js b/js/customize-sidebar-shortcuts-control.js
new file mode 100644
index 0000000..a638844
--- /dev/null
+++ b/js/customize-sidebar-shortcuts-control.js
@@ -0,0 +1,165 @@
+/* global jQuery, wp, _ */
+/* eslint no-magic-numbers: [ "error", { "ignore": [0] } ], consistent-this: [ "error", "control" ] */
+
+(function( api, $ ) {
+ 'use strict';
+
+ /**
+ * Sidebar shortcuts control extension of Dynamic Control.
+ */
+ api.controlConstructor.sidebar_shortcuts = api.controlConstructor.dynamic.extend({
+
+ /**
+ * Initialize.
+ *
+ * @param {string} id Control ID.
+ * @param {object} options Options.
+ * @param {object} options.params Params.
+ * @returns {void}
+ */
+ initialize: function( id, options ) {
+ var control = this, opt;
+
+ if ( ! api.Widgets ) {
+ throw new Error( 'The widgets component is not loaded.' );
+ }
+
+ opt = {};
+ opt.params = _.extend(
+ {
+ type: 'sidebar_shortcuts',
+ content_template: wp.template( 'customize-sidebar-shortcuts-control' ),
+ label: api.Posts.data.l10n.fieldWidgetAreasLabel,
+ active: true
+ },
+ options.params || {}
+ );
+
+ api.controlConstructor.dynamic.prototype.initialize.call( control, id, opt );
+ },
+
+ /**
+ * Ready.
+ *
+ * @returns {void}
+ */
+ ready: function() {
+ var control = this;
+ api.controlConstructor.dynamic.prototype.ready.call( control );
+
+ control.activeSidebarTemplate = wp.template( 'customize-sidebar-shortcuts-control-active-sidebar' );
+ control.widgetAreasContainer = control.container.find( 'ul.active-sidebar-sections' );
+ control.noSidebarsRenderedNotice = control.container.find( '.no-sidebars-rendered-notice' );
+
+ control.widgetAreasContainer.on( 'click', 'button', function() {
+ var button = $( this ), section, returnPromise;
+ section = api.section( button.data( 'section-id' ) );
+ returnPromise = control.focusConstructWithBreadcrumb( section, control );
+ returnPromise.done( function() {
+ button.focus();
+ } );
+
+ } );
+
+ _.bindAll(
+ control,
+ 'handleSidebarSectionAdd',
+ 'handleSidebarSectionRemove',
+ 'renderSidebarButtons'
+ );
+ control.renderSidebarButtons = _.debounce( control.renderSidebarButtons );
+
+ api.section.each( control.handleSidebarSectionAdd );
+ api.section.bind( 'add', control.handleSidebarSectionAdd );
+ api.section.bind( 'remove', control.handleSidebarSectionRemove );
+ },
+
+ /**
+ * Handle sidebar section added.
+ *
+ * @param {wp.customize.Section} section Section.
+ * @returns {void}
+ */
+ handleSidebarSectionAdd: function handleSidebarSectionAdd( section ) {
+ var control = this;
+ if ( section.extended( api.Widgets.SidebarSection ) ) {
+ section.active.bind( control.renderSidebarButtons );
+ control.renderSidebarButtons();
+ }
+ },
+
+ /**
+ * Handle sidebar section removed.
+ *
+ * @param {wp.customize.Section} section Section.
+ * @returns {void}
+ */
+ handleSidebarSectionRemove: function handleSidebarSectionRemove( section ) {
+ var control = this;
+ if ( section.extended( api.Widgets.SidebarSection ) ) {
+ section.active.unbind( control.renderSidebarButtons );
+ control.renderSidebarButtons();
+ }
+ },
+
+ /**
+ * Render sidebar buttons.
+ *
+ * @returns {void}
+ */
+ renderSidebarButtons: function renderSidebarButtons() {
+ var control = this, activeSections = [];
+
+ api.section.each( function( section ) {
+ if ( section.extended( api.Widgets.SidebarSection ) && section.active.get() ) {
+ activeSections.push( section );
+ }
+ } );
+
+ activeSections.sort( function( a, b ) {
+ return a.priority.get() - b.priority.get();
+ } );
+
+ control.widgetAreasContainer.empty();
+ _.each( activeSections, function( activeSection ) {
+ var li = $( $.trim( control.activeSidebarTemplate( {
+ section_id: activeSection.id,
+ sidebar_name: activeSection.params.title
+ } ) ) );
+ control.widgetAreasContainer.append( li );
+ } );
+
+ control.widgetAreasContainer.toggle( 0 !== activeSections.length );
+ control.noSidebarsRenderedNotice.toggle( 0 === activeSections.length );
+ },
+
+ /**
+ * Focus (expand) one construct and then focus on another construct after the first is collapsed.
+ *
+ * This overrides the back button to serve the purpose of breadcrumb navigation.
+ * This is modified from WP Core.
+ *
+ * @link https://github.com/xwp/wordpress-develop/blob/e7bbb482d6069d9c2d0e33789c7d290ac231f056/src/wp-admin/js/customize-widgets.js#L2143-L2193
+ * @param {wp.customize.Section|wp.customize.Panel|wp.customize.Control} focusConstruct - The object to initially focus.
+ * @param {wp.customize.Section|wp.customize.Panel|wp.customize.Control} returnConstruct - The object to return focus.
+ * @returns {void}
+ */
+ focusConstructWithBreadcrumb: function focusConstructWithBreadcrumb( focusConstruct, returnConstruct ) {
+ var deferred = $.Deferred(), onceCollapsed;
+ focusConstruct.focus();
+ onceCollapsed = function( isExpanded ) {
+ if ( ! isExpanded ) {
+ focusConstruct.expanded.unbind( onceCollapsed );
+ returnConstruct.focus( {
+ completeCallback: function() {
+ deferred.resolve();
+ }
+ } );
+ }
+ };
+ focusConstruct.expanded.bind( onceCollapsed );
+ return deferred;
+ }
+ });
+
+})( wp.customize, jQuery );
diff --git a/php/class-customize-posts-plugin.php b/php/class-customize-posts-plugin.php
index c8b30ba..775f54c 100644
--- a/php/class-customize-posts-plugin.php
+++ b/php/class-customize-posts-plugin.php
@@ -246,9 +246,21 @@ public function register_scripts( WP_Scripts $wp_scripts ) {
$in_footer = 1;
$wp_scripts->add( $handle, $src, $deps, $this->version, $in_footer );
+ $handle = 'customize-sidebar-shortcuts-control';
+ $src = plugins_url( 'js/customize-sidebar-shortcuts-control' . $suffix, dirname( __FILE__ ) );
+ $deps = array( 'customize-dynamic-control', 'jquery' );
+ $in_footer = 1;
+ $wp_scripts->add( $handle, $src, $deps, $this->version, $in_footer );
+
$handle = 'customize-post-section';
$src = plugins_url( 'js/customize-post-section' . $suffix, dirname( __FILE__ ) );
- $deps = array( 'customize-controls', 'customize-post-date-control', 'customize-post-status-control', 'customize-post-editor-control' );
+ $deps = array(
+ 'customize-controls',
+ 'customize-post-date-control',
+ 'customize-post-status-control',
+ 'customize-post-editor-control',
+ 'customize-sidebar-shortcuts-control',
+ );
$in_footer = 1;
$wp_scripts->add( $handle, $src, $deps, $this->version, $in_footer );
diff --git a/php/class-wp-customize-posts.php b/php/class-wp-customize-posts.php
index df2612d..801a1a1 100644
--- a/php/class-wp-customize-posts.php
+++ b/php/class-wp-customize-posts.php
@@ -612,6 +612,7 @@ public function enqueue_scripts() {
$exports = array(
'postTypes' => $post_types,
+ 'themeSupportsWidgets' => current_theme_supports( 'widgets' ),
'postStatusChoices' => $this->get_post_status_choices(),
'authorChoices' => $this->get_author_choices(),
'dateMonthChoices' => $this->get_date_month_choices(),
@@ -625,6 +626,7 @@ public function enqueue_scripts() {
'fieldStatusLabel' => __( 'Status', 'customize-posts' ),
'fieldDateLabel' => __( 'Date', 'customize-posts' ),
'fieldContentLabel' => __( 'Content', 'customize-posts' ),
+ 'fieldWidgetAreasLabel' => __( 'Widget Areas', 'customize-posts' ),
'fieldExcerptLabel' => __( 'Excerpt', 'customize-posts' ),
'fieldDiscussionLabel' => __( 'Discussion', 'customize-posts' ),
'fieldAuthorLabel' => __( 'Author', 'customize-posts' ),
@@ -793,6 +795,21 @@ public function render_templates() {
+
+
+
+