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() { + + + +