Interactive examples of custom dialogs
Basic interactive dialog
The following example demonstrates how data flows through the dialog and how buttons are configured. This is an interactive dialog that inserts the name of a cat into the editor content on submit.
-
TinyMCE
-
HTML
-
JS
-
Edit on CodePen
<textarea id="dialog-pet-machine">
<p>Click on the custom {;} toolbar button</p>
</textarea>
/* example dialog that inserts the name of a Pet into the editor content */
const dialogConfig = {
title: 'Pet Name Machine',
body: {
type: 'panel',
items: [
{
type: 'input',
name: 'catdata',
label: 'enter the name of a cat'
},
{
type: 'checkbox',
name: 'isdog',
label: 'tick if cat is actually a dog'
}
]
},
buttons: [
{
type: 'cancel',
name: 'closeButton',
text: 'Cancel'
},
{
type: 'submit',
name: 'submitButton',
text: 'Do Cat Thing',
buttonType: 'primary'
}
],
initialData: {
catdata: 'initial Cat',
isdog: false
},
onSubmit: (api) => {
const data = api.getData();
const pet = data.isdog ? 'dog' : 'cat';
tinymce.activeEditor.execCommand('mceInsertContent', false, `<p>My ${pet}'s name is: <strong>${data.catdata}</strong></p>`);
api.close();
}
};
tinymce.init({
selector: 'textarea#dialog-pet-machine',
toolbar: 'dialog-example-btn',
setup: (editor) => {
editor.ui.registry.addButton('dialog-example-btn', {
icon: 'code-sample',
onAction: () => editor.windowManager.open(dialogConfig)
})
},
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});
The dialog in this example contains two interactive components - an input component named catdata
and a checkbox component named isdog
. These names are used in the initialdata
configuration property to set the initial values for these components. In this case, when the dialog loads the input will contain the text initial Cat and the checkbox will not be checked.
The dialog also contains two footer buttons - a submit type button and a cancel type button. Since the dialog’s configuration does not contain an onCancel
callback, clicking the cancel type button will just close the dialog. However, the configuration does contain an onSubmit
callback that will be fired when the submit type button is clicked.
In the onSubmit
callback, the dialog instance API that is passed into the callback is used to call getData()
. This function returns the dialog’s data store, from which we are able to get the state of the isdog
checkbox and the value of the catadata
input. This information is used to construct a sentence which is then inserted into the editor. Finally, close()
is called to manually close the dialog.
Interactive example using Redial
Redial can be used to change information that is displayed in the dialog, create a multipage form where the next button loads a new form page, or to re-create the dialog with different components or options.
The following example demonstrates one way of implementing a multipage form dialog using the redial()
method. Custom buttons are used to switch between the two pages of the form by calling redial()
with the appropriate dialog configuration.
To see the output of the code, click on the TinyMCE tab on the fiddle below.
-
TinyMCE
-
HTML
-
JS
-
Edit on CodePen
<textarea id="redial-demo">
<p>Click on the custom {;} toolbar button to open a dialog that uses Redial to render a multipage form.</p>
</textarea>
const page1Config = {
title: 'Redial Demo',
body: {
type: 'panel',
items: [{
type: 'htmlpanel',
html: '<p>Redial allows for the contents of a dialog to be replaced with new contents. This can be used to create multipage form dialogs.</p><br/><p>The Next button is initially disabled. When the <strong>checkbox</strong> is checked, the Next button should be enabled.</p>'
}, {
type: 'checkbox',
name: 'anyterms',
label: 'I agree to disagree'
}, {
type: 'htmlpanel',
html: '<p>Pressing the Next button will call redial() to reload the dialog with the next page of the form.</p><br><p>Press Next to continue.</p>'
}]
},
initialData: {
anyterms: false
},
buttons: [
{
type: 'custom',
name: 'doesnothing',
text: 'Previous',
enabled: false
},
{
type: 'custom',
name: 'uniquename',
text: 'Next',
enabled: false
}
],
onChange: (dialogApi, details) => {
const data = dialogApi.getData();
/* Example of enabling and disabling a button, based on the checkbox state. */
dialogApi.setEnabled('uniquename', data.anyterms);
},
onAction: (dialogApi, details) => {
if (details.name === 'uniquename') {
dialogApi.redial(page2Config);
} else if (details.name === 'doesnothing') {
/* this case should never be met as the button is never enabled. */
}
}
};
const page2Config = {
title: 'Redial Demo - Page 2',
body: {
type: 'panel',
items: [
{
type: 'selectbox',
name: 'choosydata',
label: 'Choose a pet',
items: [
{ value: 'meow', text: 'Cat' },
{ value: 'woof', text: 'Dog' },
{ value: 'thunk', text: 'Rock' }
]
},
{
type: 'htmlpanel',
html: '<p>Click done and the dialog will log a message to the console, insert a sentence into the editor and close.</p>'
}
]
},
buttons: [
{
type: 'custom',
name: 'lastpage',
text: 'Done',
enabled: true
}
],
initialData: {
choosydata: ''
},
onAction: (dialogApi, details) => {
const data = dialogApi.getData();
const result = 'You chose wisely: ' + data.choosydata;
console.log(result);
tinymce.activeEditor.execCommand('mceInsertContent', false, `<p>${result}</p>`);
dialogApi.close();
}
};
tinymce.init({
selector: 'textarea#redial-demo',
toolbar: 'wizardExample',
height: '900px',
setup: (editor) => {
editor.ui.registry.addButton('wizardExample', {
icon: 'code-sample',
onAction: () => editor.windowManager.open(page1Config)
})
},
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});
The example JavaScript code contains two dialog configurations - page1Config
and page2Config
. The TinyMCE initialization code adds a button to the editor that when clicked calls editor.windowManager.open(page1Config)
to open a dialog using the first configuration.
The configuration for the first page of the multipage form contains a description of the form and a checkbox. The checkbox, via the dialog’s onChange()
callback function, toggles whether the next
button is disabled or enabled. The next
button when clicked fires the onAction()
callback function, which in turn triggers redial()
which will replace the page1Config
dialog with the page2Config
dialog.
More specifically:
The onChange()
callback in page1Config
is fired when the checkbox is toggled. It uses setEnabled
from the dialog instance API to disable and enable the Next button. The code uses getData()
from the dialog instance API to get the state of the checkbox called anyterms
(which is true
if checked and false
if unchecked) and then calls the setEnabled
function with the component name uniquename
and checkbox value to set the enabled state of the Next button.
The onAction()
callback in page1Config
is fired when either of the footer buttons are clicked, since they are both custom type footer buttons. onAction()
is passed the dialog instance API and an object containing some data about the change event, including the name
of the component that triggered it. This is important since the same onAction()
handler is shared across all compatible dialog components. The code checks the name
of the component that triggered onAction()
and if it is uniquename
(the name of the Next button) redial(page2Config)
is called. If the component’s name
is donothing
then the code does nothing.
In page2Config
the onAction()
callback uses getData()
to get the value of the selectbox
component, and specifically whether the user has chosen Cat, Dog or Rock. It then constructs a sentence using this value, inserts it into the editor content and calls close()
to manually close the dialog.