Skip to content
This repository has been archived by the owner on Mar 9, 2024. It is now read-only.

Commit

Permalink
Fix Restore Tinymce editor in text widget (#275)
Browse files Browse the repository at this point in the history
* Update script-loader.php to include scripts required to run TinyMCE in the text widget

See Issue #273

* Update text-widgets.js to run TinyMCE editor

* Update script-loader.php to remove unrelated code

* Update text-widgets.js to add TinyMCE bypasses

* Update text-widgets.js

This uses a combination of vanilla JS and jQuery, but that's what the widget uses already. (Yes, the code we've inherited here is a real dog's breakfast.) At a later date we can work on replacing this with purely vanilla JS once we have it all working.

This still leaves one thing not working correctly. If two Text widgets are added in the Customizer, the second won't open; that probably needs a fix in a different file. I will investigate.

* Coding standards fix

* Update script-loader.php in response to #275 (review)

* Update text-widgets.js

* Update customize-widgets.js to make TinyMCE work for all Text widgets in Customizer

* Update text-widgets.js to delay instantiating TinyMCE for 200ms

* Update widgets.css to prevent widgets expanding beyond container

---------

Co-authored-by: mattyrob <[email protected]>
  • Loading branch information
KTS915 and mattyrob authored Nov 10, 2023
1 parent 67d0b90 commit 77d389b
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/wp-admin/css/widgets.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* General Widgets Styles */

.widget {
margin: 0 auto 10px;
margin: 0 auto 10px !important;
position: relative;
box-sizing: border-box;
}
Expand Down
38 changes: 8 additions & 30 deletions src/wp-admin/js/customize-widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@
* @param {Object} args merged on top of this.defaultActiveArguments
*/
onChangeExpanded: function ( expanded, args ) {
var self = this, $widget, $inside, complete, prevComplete, expandControl, $details;
var self = this, $widget, $inside, complete, prevComplete, expandControl, $details, $allWidgets;

self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
if ( expanded ) {
Expand All @@ -1421,37 +1421,26 @@
$inside = $widget.find( '.widget-inside:first' );
$details = $widget.children( 'details' );

expandControl = function() {
// Close all other widget controls before expanding this one.
$allWidgets = $details.closest( 'ul' ).find( 'details');
$allWidgets.removeAttr( 'open' );

// Close all other widget controls before expanding this one.
api.control.each( function( otherControl ) {
if ( self.params.type === otherControl.params.type && self !== otherControl ) {
otherControl.collapse();
}
} );
expandControl = function() {

complete = function() {
$details.attr( 'open', 'open' );
};
if ( args.completeCallback ) {
prevComplete = complete;
complete = function () {
prevComplete();
args.completeCallback();
};
}

if ( self.params.is_wide ) {
$inside.fadeIn( args.duration, complete );
} else {
$inside.slideDown( args.duration, complete );
}
$details.attr( 'open', 'open' );

self.container.trigger( 'expand' );
self.container.addClass( 'expanding' );
};

if ( ! $details[0].hasAttribute( 'open' ) ) {
if ( ! $details.attr( 'open' ) ) {
if ( api.section.has( self.section() ) ) {
api.section( self.section() ).expand( {
completeCallback: expandControl
Expand All @@ -1460,9 +1449,6 @@
expandControl();
}
} else {
complete = function() {
$widget.removeAttr( 'open' );
};
if ( args.completeCallback ) {
prevComplete = complete;
complete = function () {
Expand All @@ -1472,15 +1458,7 @@
}

self.container.trigger( 'collapse' );

if ( self.params.is_wide ) {
$inside.fadeOut( args.duration, complete );
} else {
$inside.slideUp( args.duration, function() {
$widget.css( { width:'', margin:'' } );
complete();
} );
}
$widget.removeAttr( 'open' );
}
},

Expand Down
41 changes: 32 additions & 9 deletions src/wp-admin/js/widgets/text-widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ wp.textWidgets = ( function( $ ) {
// Sync input fields to hidden sync fields which actually get sent to the server.
_.each( control.fields, function( fieldInput, fieldName ) {
fieldInput.on( 'input change', function updateSyncField() {
var syncInput = control.syncContainer.find( '.sync-input.' + fieldName );
var syncInput = $(control.syncContainer).find( '.sync-input.' + fieldName );
if ( syncInput.val() !== fieldInput.val() ) {
syncInput.val( fieldInput.val() );
syncInput.trigger( 'change' );
}
});

// Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event.
fieldInput.val( control.syncContainer.find( '.sync-input.' + fieldName ).val() );
fieldInput.val( $(control.syncContainer).find( '.sync-input.' + fieldName ).val() );
});
},

Expand Down Expand Up @@ -216,7 +216,7 @@ wp.textWidgets = ( function( $ ) {
};

// Just-in-time force-update the hidden input fields.
control.syncContainer.closest( '.widget' ).find( '[name=savewidget]:first' ).on( 'click', function onClickSaveButton() {
control.syncContainer.closest( '.widget' ).querySelector( '[name=savewidget]' ).addEventListener( 'click', function onClickSaveButton() {
triggerChangeIfDirty();
});

Expand All @@ -235,12 +235,18 @@ wp.textWidgets = ( function( $ ) {

// The user has disabled TinyMCE.
if ( typeof window.tinymce === 'undefined' ) {
wp.oldEditor.initialize( id, {
quicktags: true,
mediaButtons: true
});

return;
}

// Destroy any existing editor so that it can be re-initialized after a widget-updated event.
if ( tinymce.get( id ) ) {
restoreTextMode = tinymce.get( id ).isHidden();
wp.oldEditor.remove( id );
}

// Add or enable the `wpview` plugin.
Expand All @@ -254,6 +260,14 @@ wp.textWidgets = ( function( $ ) {
}
} );

wp.oldEditor.initialize( id, {
tinymce: {
wpautop: true
},
quicktags: true,
mediaButtons: true
} );

/**
* Show a pointer, focus on dismiss, and speak the contents for a11y.
*
Expand Down Expand Up @@ -364,20 +378,29 @@ wp.textWidgets = ( function( $ ) {
* @return {void}
*/
component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) {
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, renderWhenAnimationDone, fieldContainer, syncContainer;
var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 200, renderWhenAnimationDone, fieldContainer, syncContainer;
widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen.

idBase = widgetContainer.find( '.id_base' ).val();
if ( widgetContainer instanceof jQuery ) {
widgetContainer = widgetContainer[0];
}

idBase = widgetContainer.querySelector( '.id_base' ).value;
if ( -1 === component.idBases.indexOf( idBase ) ) {
return;
}

// Prevent initializing already-added widgets.
widgetId = widgetForm.find( '.widget-id' ).val();
widgetId = widgetContainer.querySelector( '.widget-id' ).value;
if ( component.widgetControls[ widgetId ] ) {
return;
}

// Bypass using TinyMCE when widget is in legacy mode.
if ( ! widgetContainer.querySelector( '.visual' ).value ) {
return;
}

/*
* Create a container element for the widget control fields.
* This is inserted into the DOM immediately before the .widget-content
Expand All @@ -389,8 +412,8 @@ wp.textWidgets = ( function( $ ) {
* components", the JS template is rendered outside of the normal form
* container.
*/
fieldContainer = $( '<div></div>' );
syncContainer = widgetContainer.find( '.widget-content:first' );
fieldContainer = document.createElement( 'div' );
syncContainer = widgetContainer.querySelector( '.widget-content' );
syncContainer.before( fieldContainer );

widgetControl = new component.TextWidgetControl({
Expand All @@ -407,7 +430,7 @@ wp.textWidgets = ( function( $ ) {
* with TinyMCE being able to set contenteditable on it.
*/
renderWhenAnimationDone = function() {
if ( ! widgetContainer.hasClass( 'open' ) ) {
if ( ! widgetContainer.querySelector( 'details' ).hasAttribute( 'open' ) ) {
setTimeout( renderWhenAnimationDone, animatedCheckDelay );
} else {
widgetControl.initializeEditor();
Expand Down
17 changes: 17 additions & 0 deletions src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,23 @@ function wp_default_packages_inline_scripts( $scripts ) {
),
'after'
);

// Loading the old editor and its config to ensure the classic block works as expected.
$scripts->add_inline_script(
'editor',
'window.wp.oldEditor = window.wp.editor;',
'after'
);

// wp-editor module is exposed as window.wp.editor
// Problem: there is quite some code expecting window.wp.oldEditor object available under window.wp.editor
// Solution: fuse the two objects together to maintain backward compatibility
// For more context, see https://github.com/WordPress/gutenberg/issues/33203
$scripts->add_inline_script(
'wp-editor',
'Object.assign( window.wp.editor, window.wp.oldEditor );',
'after'
);
}

/**
Expand Down

0 comments on commit 77d389b

Please sign in to comment.