mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/WikiEditor
synced 2024-11-23 15:57:15 +00:00
Add toggle button and two-pane layout for realtime preview
* Toolbar button to toggle a two-pane layout on and off. * Resizable bar for the width of the two panes. * Resizable bar for the edit box height, even when realtime preview is off. * Only enabled when $wgWikiEditorRealtimePreview = true. * Fires JS hooks when enabled, resized, and disabled. Bug: T293347 Change-Id: Id09d44519249c0b7f5c33d48d524b7c92a5a9106
This commit is contained in:
parent
27020ae196
commit
411be83bc3
|
@ -13,7 +13,7 @@
|
|||
"license-name": "GPL-2.0-or-later",
|
||||
"type": "editor",
|
||||
"requires": {
|
||||
"MediaWiki": ">= 1.37"
|
||||
"MediaWiki": ">= 1.38.0"
|
||||
},
|
||||
"MessagesDirs": {
|
||||
"WikiEditor": [
|
||||
|
@ -300,6 +300,26 @@
|
|||
"ext.wikiEditor.styles": {
|
||||
"group": "ext.wikiEditor",
|
||||
"styles": "ext.wikiEditor.toolbar.styles.less"
|
||||
},
|
||||
"ext.wikiEditor.realtimepreview": {
|
||||
"dependencies": [
|
||||
"ext.wikiEditor",
|
||||
"mediawiki.page.preview"
|
||||
],
|
||||
"messages": [
|
||||
"wikieditor-realtimepreview-preview"
|
||||
],
|
||||
"packageFiles": [
|
||||
"realtimepreview/init.js",
|
||||
"realtimepreview/RealtimePreview.js",
|
||||
"realtimepreview/ResizingDragBar.js",
|
||||
"realtimepreview/TwoPaneLayout.js"
|
||||
],
|
||||
"styles": [
|
||||
"realtimepreview/RealtimePreview.less",
|
||||
"realtimepreview/ResizingDragBar.less",
|
||||
"realtimepreview/TwoPaneLayout.less"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ResourceFileModulePaths": {
|
||||
|
@ -389,5 +409,11 @@
|
|||
"AutoloadNamespaces": {
|
||||
"MediaWiki\\Extension\\WikiEditor\\": "includes/"
|
||||
},
|
||||
"config": {
|
||||
"WikiEditorRealtimePreview": {
|
||||
"description": "Whether to enable the Realtime Preview feature.",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"manifest_version": 2
|
||||
}
|
||||
|
|
|
@ -197,5 +197,6 @@
|
|||
"wikieditor-toolbar-help-content-indent-syntax": "Normal text<br />:Indented text<br />::Indented text",
|
||||
"wikieditor-toolbar-help-content-indent-result": "Normal text<dl><dd>Indented text<dl><dd>Indented text</dd></dl></dd></dl>",
|
||||
"tag-wikieditor": "-",
|
||||
"tag-wikieditor-description": "Edit made using [[mw:Special:MyLanguage/Extension:WikiEditor|WikiEditor]] (2010 wikitext editor)"
|
||||
"tag-wikieditor-description": "Edit made using [[mw:Special:MyLanguage/Extension:WikiEditor|WikiEditor]] (2010 wikitext editor)",
|
||||
"wikieditor-realtimepreview-preview": "Preview"
|
||||
}
|
||||
|
|
|
@ -228,5 +228,6 @@
|
|||
"wikieditor-toolbar-help-content-indent-syntax": "{{RawHtml|phab=T294760}}\n\nSyntax example used in the help section \"discussion\" of the toolbar",
|
||||
"wikieditor-toolbar-help-content-indent-result": "{{RawHtml|phab=T294760}}\n\nHTML example used in the help section \"discussion\" of the toolbar",
|
||||
"tag-wikieditor": "{{ignored}}Short description of the wikieditor tag.\n\nShown on lists of changes (history, recentchanges, etc.) for each edit made using WikiEditor.\n\nSee also:\n* {{msg-mw|Tag-wikieditor-description}}",
|
||||
"tag-wikieditor-description": "Long description of the wikieditor tag ({{msg-mw|Tag-wikieditor}}).\n\nShown on [[Special:Tags]].\n\nSee also:\n* {{msg-mw|Tag-wikieditor}}"
|
||||
"tag-wikieditor-description": "Long description of the wikieditor tag ({{msg-mw|Tag-wikieditor}}).\n\nShown on [[Special:Tags]].\n\nSee also:\n* {{msg-mw|Tag-wikieditor}}",
|
||||
"wikieditor-realtimepreview-preview": "Label for the toolbar button to enable/disable real-time preview."
|
||||
}
|
||||
|
|
|
@ -222,6 +222,10 @@ class Hooks implements
|
|||
if ( $this->userOptionsLookup->getBoolOption( $user, 'usebetatoolbar' ) ) {
|
||||
$outputPage->addModuleStyles( 'ext.wikiEditor.styles' );
|
||||
$outputPage->addModules( 'ext.wikiEditor' );
|
||||
// Optionally enable Realtime Preview.
|
||||
if ( $this->config->get( 'WikiEditorRealtimePreview' ) ) {
|
||||
$outputPage->addModules( 'ext.wikiEditor.realtimepreview' );
|
||||
}
|
||||
}
|
||||
|
||||
// Don't run this if the request was posted - we don't want to log 'init' when the
|
||||
|
|
134
modules/realtimepreview/RealtimePreview.js
Normal file
134
modules/realtimepreview/RealtimePreview.js
Normal file
|
@ -0,0 +1,134 @@
|
|||
var ResizingDragBar = require( './ResizingDragBar.js' );
|
||||
var TwoPaneLayout = require( './TwoPaneLayout.js' );
|
||||
|
||||
/**
|
||||
* @class
|
||||
*/
|
||||
function RealtimePreview() {
|
||||
this.enabled = false;
|
||||
this.twoPaneLayout = new TwoPaneLayout();
|
||||
this.pagePreview = require( 'mediawiki.page.preview' );
|
||||
// @todo This shouldn't be required, but the preview element is added in PHP
|
||||
// and can have attributes with values that aren't easily accessible from here,
|
||||
// and we need to duplicate here what Live Preview does in core.
|
||||
var $previewContent = $( '#wikiPreview' ).clone().html();
|
||||
this.$previewNode = $( '<div>' )
|
||||
.addClass( 'ext-WikiEditor-realtimepreview-preview' )
|
||||
.append( $previewContent );
|
||||
this.$errorNode = $( '<div>' )
|
||||
.addClass( 'error' );
|
||||
this.twoPaneLayout.getPane2().append( this.$previewNode, this.$errorNode );
|
||||
this.eventNames = 'change.realtimepreview input.realtimepreview cut.realtimepreview paste.realtimepreview';
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {Object} context The WikiEditor context.
|
||||
* @return {OO.ui.ToggleButtonWidget}
|
||||
*/
|
||||
RealtimePreview.prototype.getToolbarButton = function ( context ) {
|
||||
this.context = context;
|
||||
var $uiText = context.$ui.find( '.wikiEditor-ui-text' );
|
||||
|
||||
// Fix the height of the textarea, before adding a resizing bar below it.
|
||||
var height = context.$textarea.height();
|
||||
$uiText.css( 'height', height + 'px' );
|
||||
context.$textarea.removeAttr( 'rows cols' );
|
||||
|
||||
// Add the resizing bar.
|
||||
var bottomDragBar = new ResizingDragBar( { isEW: false } );
|
||||
$uiText.after( bottomDragBar.$element );
|
||||
|
||||
// Create and configure the toolbar button.
|
||||
this.button = new OO.ui.ToggleButtonWidget( {
|
||||
label: mw.msg( 'wikieditor-realtimepreview-preview' ),
|
||||
icon: 'article',
|
||||
value: this.enabled,
|
||||
framed: false
|
||||
} );
|
||||
this.button.connect( this, { change: this.toggle } );
|
||||
return this.button;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the two-pane preview display.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} context The WikiEditor context object.
|
||||
*/
|
||||
RealtimePreview.prototype.toggle = function () {
|
||||
var $uiText = this.context.$ui.find( '.wikiEditor-ui-text' );
|
||||
var $textarea = this.context.$textarea;
|
||||
|
||||
// Remove or add the layout to the DOM.
|
||||
if ( this.enabled ) {
|
||||
// Move height from the TwoPaneLayout to the text UI div.
|
||||
$uiText.css( 'height', this.twoPaneLayout.$element.height() + 'px' );
|
||||
|
||||
// Put the text div back to being after the layout, and then hide the layout.
|
||||
this.twoPaneLayout.$element.after( $uiText );
|
||||
this.twoPaneLayout.$element.hide();
|
||||
|
||||
// Remove the keyup handler.
|
||||
$textarea.off( this.eventNames );
|
||||
|
||||
// Let other things happen after disabling.
|
||||
mw.hook( 'ext.WikiEditor.realtimepreview.disable' ).fire( this );
|
||||
|
||||
} else {
|
||||
// Add the layout before the text div of the UI and then move the text div into it.
|
||||
$uiText.before( this.twoPaneLayout.$element );
|
||||
this.twoPaneLayout.setPane1( $uiText );
|
||||
this.twoPaneLayout.$element.show();
|
||||
|
||||
// Move explicit height from text-ui (which may have been set via manual resizing), to panes.
|
||||
this.twoPaneLayout.$element.css( 'height', $uiText.height() + 'px' );
|
||||
$uiText.css( 'height', '100%' );
|
||||
|
||||
// Enable realtime previewing.
|
||||
this.addPreviewListener( $textarea );
|
||||
|
||||
// Let other things happen after enabling.
|
||||
mw.hook( 'ext.WikiEditor.realtimepreview.enable' ).fire( this );
|
||||
}
|
||||
|
||||
// Record the toggle state and update the button.
|
||||
this.enabled = !this.enabled;
|
||||
this.button.setFlags( { progressive: this.enabled } );
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {jQuery} $editor The element to listen to changes on.
|
||||
*/
|
||||
RealtimePreview.prototype.addPreviewListener = function ( $editor ) {
|
||||
// Get preview when enabling.
|
||||
this.doRealtimePreview();
|
||||
// Also get preview on keyup, change, paste etc.
|
||||
$editor
|
||||
.off( this.eventNames )
|
||||
.on( this.eventNames, mw.util.debounce( 2000, this.doRealtimePreview.bind( this ) ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
RealtimePreview.prototype.doRealtimePreview = function () {
|
||||
this.twoPaneLayout.getPane2().addClass( 'ext-WikiEditor-twopanes-loading' );
|
||||
var loadingSelectors = this.pagePreview.getLoadingSelectors();
|
||||
loadingSelectors.push( '.ext-WikiEditor-realtimepreview-preview' );
|
||||
this.$errorNode.empty();
|
||||
this.pagePreview.doPreview( {
|
||||
$previewNode: this.$previewNode,
|
||||
$spinnerNode: false,
|
||||
loadingSelectors: loadingSelectors
|
||||
} ).fail( function ( code, result ) {
|
||||
var $errorMsg = ( new mw.Api() ).getErrorMessage( result );
|
||||
this.$previewNode.hide();
|
||||
this.$errorNode.append( $errorMsg );
|
||||
}.bind( this ) ).always( function () {
|
||||
this.twoPaneLayout.getPane2().removeClass( 'ext-WikiEditor-twopanes-loading' );
|
||||
}.bind( this ) );
|
||||
};
|
||||
|
||||
module.exports = RealtimePreview;
|
19
modules/realtimepreview/RealtimePreview.less
Normal file
19
modules/realtimepreview/RealtimePreview.less
Normal file
|
@ -0,0 +1,19 @@
|
|||
@import 'mediawiki.ui/variables.less';
|
||||
|
||||
/* stylelint-disable selector-max-id */
|
||||
#wpTextbox1,
|
||||
.mw-editform #wpTextbox1 {
|
||||
// stylelint-disable-next-line plugin/no-unsupported-browser-features
|
||||
resize: none;
|
||||
height: 100%;
|
||||
min-height: auto;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.wikiEditor-ui .wikiEditor-ui-view {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar-ns {
|
||||
border-top: 1px solid @colorGray12;
|
||||
}
|
84
modules/realtimepreview/ResizingDragBar.js
Normal file
84
modules/realtimepreview/ResizingDragBar.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @class
|
||||
* @constructor
|
||||
* @extends OO.ui.Element
|
||||
* @param {Object} [config] Configuration options
|
||||
* @param {boolean} [config.isEW] Orientation of the drag bar, East-West (true) or North-South (false).
|
||||
*/
|
||||
function ResizingDragBar( config ) {
|
||||
config = $.extend( {}, {
|
||||
isEW: true,
|
||||
classes: [ 'ext-WikiEditor-ResizingDragBar' ]
|
||||
}, config );
|
||||
ResizingDragBar.super.call( this, config );
|
||||
|
||||
var classNameDir = 'ext-WikiEditor-ResizingDragBar-' + ( config.isEW ? 'ew' : 'ns' );
|
||||
// Possible class names:
|
||||
// * ext-WikiEditor-ResizingDragBar-ew
|
||||
// * ext-WikiEditor-ResizingDragBar-ns
|
||||
this.$element.addClass( classNameDir );
|
||||
|
||||
var resizingDragBar = this;
|
||||
this.$element.on( 'mousedown', function ( eventMousedown ) {
|
||||
if ( eventMousedown.button !== ResizingDragBar.static.MAIN_MOUSE_BUTTON ) {
|
||||
// If not the main mouse (e.g. left) button, ignore.
|
||||
return;
|
||||
}
|
||||
// Prevent selecting (or anything else) when dragging over other parts of the page.
|
||||
$( document ).on( 'selectstart.' + classNameDir, false );
|
||||
// Set up parameter names.
|
||||
var xOrY = config.isEW ? 'pageX' : 'pageY';
|
||||
var widthOrHeight = config.isEW ? 'width' : 'height';
|
||||
var lastOffset = eventMousedown[ xOrY ];
|
||||
// Handle the actual dragging.
|
||||
$( document ).on( 'mousemove.' + classNameDir, function ( eventMousemove ) {
|
||||
// Initial width or height of the pane.
|
||||
var startSize = resizingDragBar.getResizedPane()[ widthOrHeight ]();
|
||||
// Current position of the mouse (relative to page, not viewport).
|
||||
var newOffset = eventMousemove[ xOrY ];
|
||||
// Distance the mouse has moved.
|
||||
var change = lastOffset - newOffset;
|
||||
// Set the new size of the pane, and tell others about it.
|
||||
var newSize = Math.max( startSize - change, ResizingDragBar.static.MIN_PANE_SIZE );
|
||||
resizingDragBar.getResizedPane().css( widthOrHeight, newSize );
|
||||
// Save the new starting point of the mouse, from which to calculate the next move.
|
||||
lastOffset = newOffset;
|
||||
// Let other scripts do things after the resize.
|
||||
mw.hook( 'ext.WikiEditor.realtimepreview.resize' ).fire( resizingDragBar );
|
||||
} );
|
||||
} );
|
||||
// Add a UI affordance within the handle area (CSS gives it its appearance).
|
||||
this.$element.append( $( '<span>' ) );
|
||||
// Remove the resize event handler when the mouse is released.
|
||||
$( document ).on( 'mouseup', function () {
|
||||
$( document ).off( 'mousemove.' + classNameDir );
|
||||
$( document ).off( 'selectstart.' + classNameDir, false );
|
||||
} );
|
||||
}
|
||||
|
||||
OO.inheritClass( ResizingDragBar, OO.ui.Element );
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @property {number} See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||
*/
|
||||
ResizingDragBar.static.MAIN_MOUSE_BUTTON = 0;
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @property {number} The minimum pane size, in pixels.
|
||||
* Should be slightly more than the affordance length.
|
||||
*/
|
||||
ResizingDragBar.static.MIN_PANE_SIZE = 100;
|
||||
|
||||
/**
|
||||
* Get the pane that is resized by this bar (always the immediate prior sibling).
|
||||
*
|
||||
* @public
|
||||
* @return {jQuery}
|
||||
*/
|
||||
ResizingDragBar.prototype.getResizedPane = function () {
|
||||
return this.$element.prev();
|
||||
};
|
||||
|
||||
module.exports = ResizingDragBar;
|
40
modules/realtimepreview/ResizingDragBar.less
Normal file
40
modules/realtimepreview/ResizingDragBar.less
Normal file
|
@ -0,0 +1,40 @@
|
|||
@import 'mediawiki.ui/variables.less';
|
||||
|
||||
// The dimensions of the UI affordance (the little line in the draggable area).
|
||||
@affordance-width: 4px;
|
||||
@affordance-length: 110px;
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar {
|
||||
background-color: @colorGray14;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar-ns {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar-ew {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar span {
|
||||
width: @affordance-length;
|
||||
height: @affordance-width;
|
||||
background-color: @colorGray12;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
// Don't change without also changing the calculated width of pane1 above.
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar:hover span {
|
||||
background-color: @colorGray5;
|
||||
}
|
||||
|
||||
.ext-WikiEditor-ResizingDragBar-ew span {
|
||||
height: @affordance-length;
|
||||
width: @affordance-width;
|
||||
border-width: 0 1px;
|
||||
}
|
79
modules/realtimepreview/TwoPaneLayout.js
Normal file
79
modules/realtimepreview/TwoPaneLayout.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
var ResizingDragBar = require( './ResizingDragBar.js' );
|
||||
|
||||
/**
|
||||
* This is a layout with two resizable panes.
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
* @extends OO.ui.Layout
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
function TwoPaneLayout( config ) {
|
||||
// Configuration initialization
|
||||
config = config || {};
|
||||
TwoPaneLayout.super.call( this, config );
|
||||
|
||||
this.$pane1 = $( '<div>' ).addClass( 'ext-WikiEditor-twopanes-pane1' );
|
||||
var middleDragBar = new ResizingDragBar( { isEW: true } );
|
||||
this.$pane2 = $( '<div>' ).addClass( 'ext-WikiEditor-twopanes-pane2' );
|
||||
|
||||
this.$element.addClass( 'ext-WikiEditor-twopanes-TwoPaneLayout' );
|
||||
this.$element.append( this.$pane1, middleDragBar.$element, this.$pane2 );
|
||||
}
|
||||
|
||||
OO.inheritClass( TwoPaneLayout, OO.ui.Layout );
|
||||
|
||||
/**
|
||||
* Set pane 1 content.
|
||||
*
|
||||
* @public
|
||||
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} content
|
||||
*/
|
||||
TwoPaneLayout.prototype.setPane1 = function ( content ) {
|
||||
this.setContent( this.$pane1, content );
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @return {jQuery}
|
||||
*/
|
||||
TwoPaneLayout.prototype.getPane1 = function () {
|
||||
return this.$pane1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set pane 2 content.
|
||||
*
|
||||
* @public
|
||||
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} content
|
||||
*/
|
||||
TwoPaneLayout.prototype.setPane2 = function ( content ) {
|
||||
this.setContent( this.$pane2, content );
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @return {jQuery}
|
||||
*/
|
||||
TwoPaneLayout.prototype.getPane2 = function () {
|
||||
return this.$pane2;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {jQuery} $container The container to set the content in.
|
||||
* @param {jQuery|string|Function|OO.ui.HtmlSnippet} content The content to set.
|
||||
*/
|
||||
TwoPaneLayout.prototype.setContent = function ( $container, content ) {
|
||||
if ( typeof content === 'string' ) {
|
||||
$container.text( content );
|
||||
} else if ( content instanceof OO.ui.HtmlSnippet ) {
|
||||
$container.html( content.toString() );
|
||||
} else if ( content instanceof $ ) {
|
||||
$container.empty().append( content );
|
||||
} else {
|
||||
$container.empty();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = TwoPaneLayout;
|
53
modules/realtimepreview/TwoPaneLayout.less
Normal file
53
modules/realtimepreview/TwoPaneLayout.less
Normal file
|
@ -0,0 +1,53 @@
|
|||
@import 'mediawiki.ui/variables.less';
|
||||
|
||||
.ext-WikiEditor-twopanes-TwoPaneLayout {
|
||||
// stylelint-disable-next-line plugin/no-unsupported-browser-features
|
||||
display: flex;
|
||||
|
||||
.ext-WikiEditor-twopanes-pane1 {
|
||||
border: 1px solid @colorGray12;
|
||||
border-width: 0 1px 0 0;
|
||||
// Offset by half the difference in padding and the UI affordance.
|
||||
// stylelint-disable-next-line plugin/no-unsupported-browser-features
|
||||
width: ~'calc( 50% - 9px )';
|
||||
}
|
||||
|
||||
.ext-WikiEditor-twopanes-pane2 {
|
||||
position: relative;
|
||||
border: 1px solid @colorGray12;
|
||||
border-width: 0 0 0 1px;
|
||||
flex: 1 1 0;
|
||||
overflow: auto;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
@loadingbar-width: 20%;
|
||||
|
||||
// stylelint-disable-next-line selector-pseudo-element-colon-notation
|
||||
.ext-WikiEditor-twopanes-pane2.ext-WikiEditor-twopanes-loading::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
display: block;
|
||||
opacity: 1;
|
||||
content: ' ';
|
||||
background-color: @color-primary;
|
||||
width: @loadingbar-width;
|
||||
height: 4px;
|
||||
// Hide the loading bar to start with; it'll be shown if the loading state persists for more than 1s.
|
||||
visibility: hidden;
|
||||
animation: loadingbar 1.5s 1s infinite linear alternate;
|
||||
}
|
||||
|
||||
@keyframes loadingbar {
|
||||
0% {
|
||||
visibility: visible;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
// stylelint-disable-next-line plugin/no-unsupported-browser-features
|
||||
left: calc( 100% - @loadingbar-width );
|
||||
}
|
||||
}
|
||||
}
|
16
modules/realtimepreview/init.js
Normal file
16
modules/realtimepreview/init.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
mw.hook( 'wikiEditor.toolbarReady' ).add( function ( $textarea ) {
|
||||
var RealtimePreview = require( './RealtimePreview.js' );
|
||||
var realtimePreview = new RealtimePreview();
|
||||
$textarea.wikiEditor( 'addToToolbar', {
|
||||
section: 'secondary',
|
||||
group: 'default',
|
||||
tools: {
|
||||
realtimepreview: {
|
||||
type: 'element',
|
||||
element: function ( context ) {
|
||||
return realtimePreview.getToolbarButton( context ).$element;
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
} );
|
Loading…
Reference in a new issue