NextGen Editor Documentation
How to use and integrate your plugins with the NextGen Editor
Found an issue or a problem that you can't find an answer for? Create an issue in our Premium Issue Tracker.
Before you start
Before you can install a Premium Plugin, you are required to have the License Manager
plugin installed. It is a free plugin available in GPM and can be installed like any other through the Plugin section of the admin plugin, or via command line.
$ bin/gpm install license-manager
Installation
First ensure you are running the latest version of Grav 1.6 or Grav 1.7 (-f
forces a refresh of the GPM index).
$ bin/gpm selfupgrade -f
The NextGen Editor doesn't have any dependencies, so installation is a simple process of running the command:
$ bin/gpm install nextgen-edtior
You can also install the plugin via the Plugins section in the admin plugin.
Configuration
Before configuring this plugin, you should copy the user/plugins/ck-editor5/ck-editor5.yaml
to user/config/plugins/ck-editor5.yaml
and only edit that copy.
Here is the default configuration and an explanation of available options:
enabled: true
Note that if you use the admin plugin, a file with your configuration, and named ck-editor5.yaml will be saved in the user/config/plugins/
folder once the configuration is saved in the admin.
Usage
After installation, the default behavior sets the NextGen Editor as the Default for All. You can change this behavior in the plugin configuration.
If you are running Grav 1.7, there is a new option that lets you configure which editor to use per-user. Simply click on your user profile (or the user in the Users list if you are using FlexUsers), and choose the editor you prefer to use:
Keyboard Shortcuts
Platform Specific Shortcuts
Action | PC | Mac |
---|---|---|
Save | Ctrl + S |
⌘ + S |
Copy | Ctrl + C |
⌘ + C |
Paste | Ctrl + V |
⌘ + V |
Copy + Cut | Ctrl + X |
⌘ + X |
Undo | Ctrl + Z |
⌘ + Z |
Redo | Ctrl + Y |
⌘ + V |
Ctrl + Shift + Z |
⌘ + Shift + Z |
|
Bold | Ctrl + B |
⌘ + B |
Italic | Ctrl + I |
⌘ + I |
Link | Ctrl + K |
⌘ + K |
General Shortcuts
Action | Keyboard |
---|---|
Insert a hard break (e.g. a new paragraph) | Enter |
Insert a soft break (e.g. carriage return) | Shift + Enter |
Nest the current list item (when in a list) | Tab |
Widget/Shortcode Shortcuts
Action | Keyboard |
---|---|
Insert a new paragraph directly after a widget | Enter |
Insert a new paragraph directly before a widget | Shift + Enter |
Display the caret to allow typing directly before a widget | ↑ / ← |
Display the caret to allow typing directly after a widget | ↓ / → |
Table Cell Shortcuts
Action | Keyboard |
---|---|
Move the selection to the next cell | Tab |
Move the selection to the previous cell | Shift + Tab |
Insert a new table row (when in the last cell of a table) | Tab |
Navigate through the table | ↑ / → / ↓ / ← |
User Interface and Navigation Shortcuts
Action | Keyboard |
---|---|
Close contextual balloons and UI components like dropdowns | Esc |
Move focus to the visible contextual balloon | Tab |
Move focus between fields in contextual balloons | Tab |
Move focus to the toolbar | Alt + F10 |
Navigate through the toolbar | ↑ / → / ↓ / ← |
Execute the currently focused button | Enter |
Editor Options
Here you can specify:
Fixed Height
- The ability to show only a fixed height editor and a scrollbar, rather than the full height editor which is the default behaviorStikcy Toolbar
- Particularly useful for the default full-height editor, the toolbar will stick to the page as you scroll down large content.Toolbar
- Gives you the ability to remove specific toolbar items. The default list displays all the currently available toolbar items.-
Show HTML Snippets Preview
- By default, HTML Snippets will be displayed as HTML code. When this setting is enabled, NextGen Editor will render the HTML within the editor. Both views will have a pencil icon that allows for editing the HTML directly.Even with previews enable, JavaScript won't be executed.
Automatic Text Transformations
It is possible to enable and disable sets of text transformations, conveniently grouped by typhography, quotations, symbols, mathematical. You can also create custom text transformations to meet your need. Whenever the input text matches a text transformation, this will get automatically converted to the appropriate output.
Markdown → Editor Options
These options control the loading behavior when NextGen Editor loads markdown from the .md
file and converts it into a WYSIWYM editor environment.
Line Breaks
- Convert\n
in paragraphs into<br>
HTML tagsLinkify Text Links
- Automatically turn text links into HTML linksTypographer
- Enable some language-neutral replacement + quotes beautificationCodeblock Language Prefix
- what to prefix codeblocks withFancy Quotes
- Fancy quotes to be converted to regular quotesHighlight Function
- Advanced use only, change this only if you know what you are doing
Editor → Markdown Options
These options control the saving behavior when NextGen Editor saves the markdown to the .md
file from the WYSIWYM editor environment. These control the markdown format behavior.
HTML Snippets
At some point during writing content, you will find yourself needing to save raw HTML in your content but do not want said HTML to get converted to Markdown, nor having the HTML get modified by any internal operations.
NextGen Editor makes this possible thanks to the HTML Snippets
, a custom shortcut available in the toolbar that allows to write any HTML that, upon saving, gets wrapped by a div
with class name raw-html-embed
.
Extending NextGen Editor
The NextGen Editor has powerful functionality that allows it to be extended via plugins. Good examples of this capability can be found in both Shortcode Core
and Page Inject
plugins. These both already have support for NextGen editor custom blocks.
There a single plugin events that you can utilize to register your plugin's custom functionality: registerNextGenEditorPlugin
. In Shortcode Core plugin, two methods are called from this one event. One to register some global functionality, and another to register specific shortcodes:
public function registerNextGenEditorPlugin($event) {
$config = $this->config->get('plugins.shortcode-core.nextgen-editor');
$plugins = $event['plugins'];
if ($config['env'] !== 'development') {
$plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/dist/css/app.css';
$plugins['js'][] = 'plugin://shortcode-core/nextgen-editor/dist/js/app.js';
} else {
$plugins['js'][] = 'http://' . $config['dev_host'] . ':' . $config['dev_port'] . '/js/app.js';
}
$event['plugins'] = $plugins;
return $event;
}
In this example, some core CSS
and JS
functionality is added to the plugins
element on the $event
object that is passed to the event. This particular example even has the ability to load a dynamic app.js
when in development mode.
The Shortcode Core plugin is providing everything you need to handle custom shortcode logic, so you don't need to repeat this functionality if all you developing a shortcode plugin. The Shortcode UI
plugin is a custom shortcode plugin that relies on Shortcode Core
for it's core functionality and also relies on it for it's core support for the NextGen Editor.
The Shortcode UI
plugin listens for the registerNextGenEditorPlugin
event and calls the registerNextGenEditorPluginShortcodes
method.:
public static function getSubscribedEvents()
{
return [
...
'registerNextGenEditorPlugin' => ['registerNextGenEditorPluginShortcodes', 0],
];
}
Then this method simply adds the JS that controls the shortcode handling, and optionally any CSS that may be required:
public function registerNextGenEditorPluginShortcodes($event) {
$plugins = $event['plugins'];
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/shortcode-ui.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-accordion/ui-accordion.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-tabs/ui-tabs.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-browser/ui-browser.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-polaroid/ui-polaroid.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-animated-text/ui-animated-text.js';
$plugins['js'][] = 'plugin://shortcode-ui/nextgen-editor/shortcodes/ui-image-compare/ui-image-compare.js';
$event['plugins'] = $plugins;
return $event;
}
The first JavaScript file shortcode-ui.js
contains the logic to create the plugin reference and adds a button group that will used by all the subsequent shortcodes so they can appear in a dropdown:
window.nextgenEditor.addHook('hookInit', () => {
window.nextgenEditor.addShortcodePlugin('shortcode-ui', {
title: 'Shortcode UI',
});
window.nextgenEditor.addButtonGroup('shortcode-ui', {
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.5 19h19a2.5 2.5 0 002.5-2.5v-14A2.5 2.5 0 0021.5 0h-19A2.5 2.5 0 000 2.5v14A2.5 2.5 0 002.5 19zM18.25 7.5a.75.75 0 011.28-.53l2 2a.749.749 0 010 1.06l-2 2a.746.746 0 01-.53.22.738.738 0 01-.287-.057.75.75 0 01-.463-.693zM2.47 8.97l2-2a.75.75 0 011.28.53v4a.75.75 0 01-.463.693.738.738 0 01-.287.057.746.746 0 01-.53-.22l-2-2a.749.749 0 010-1.06z"/><circle cx="7.5" cy="22.5" r="1.5"/><circle cx="12" cy="22.5" r="1.5"/><circle cx="16.5" cy="22.5" r="1.5"/></svg>',
label: 'Shortcode UI',
});
});
The individual shortcode JavaScript files contain the logic that is pertinent to that shortcode. A relatively simple example is the UI Browser
shortcode (ui-browser/ui-browser.js
):
window.nextgenEditor.addShortcode('ui-browser', {
type: 'block',
plugin: 'shortcode-ui',
title: 'UI Browser',
button: {
group: 'shortcode-ui',
label: 'UI Browser',
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="4" y="8.25" style="width:16px;height:3px;" rx=".5" ry=".5"/><path d="M13.25 12.75a.75.75 0 000 1.5h6a.75.75 0 000-1.5zM19.25 16.25h-6a.75.75 0 000 1.5h6a.75.75 0 000-1.5z"/><rect x="4" y="12.75" style="width:7px;height:6.5px" rx="1" ry="1"/><path d="M24 4.75a3 3 0 00-3-3H3a3 3 0 00-3 3v14.5a3 3 0 003 3h18a3 3 0 003-3zm-14.346-1a.966.966 0 011.692 0 .969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0 .969.969 0 01-.154-.5.969.969 0 01.154-.5zm-3.5 0a.966.966 0 011.692 0 .969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0A.969.969 0 016 4.25a.969.969 0 01.154-.5zm-3.562.092A1 1 0 013.5 3.25a.985.985 0 01.846.5.969.969 0 01.154.5.969.969 0 01-.154.5.966.966 0 01-1.692 0 .969.969 0 01-.154-.5.979.979 0 01.092-.408zM22 19.25a1 1 0 01-1 1H3a1 1 0 01-1-1V7a.25.25 0 01.25-.25h19.5A.25.25 0 0122 7z"/></svg>',
},
attributes: {
address: {
type: String,
title: 'Address',
widget: 'input-text',
default: '',
},
class: {
type: String,
title: 'Class',
widget: 'input-text',
default: '',
},
},
titlebar({ attributes }) {
return `address: <strong>${attributes.address}</strong>`;
},
content() {
return `<div>{{content_editable}}</div>`;
},
});
Configuration Details:
name (String)
- shortcode nameschema (Object)
- shortcode schematype (String)
- shortcode type ('block'
or'inline'
)title (String)
- shortcode titleparent (String)
- parent shortcode nameattributes (Object)
- shortcode attributestype (JS Type)
- attribute value typetitle (String)
- attribute titlewidget (String|Object)
- attribute editing widget type or schematype (String)
- widget typevisible (Function)
- whether the widget is displayed in the settings popup(arg) attributes
- shortcode attribute values(arg) parentAttributes
- parent shortcode attribute values (if shortcode has parent in schema)
default (Any|Object)
- attribute default valuevalue (Any)
- attribute default valuepreserve (Boolean)
- whether to cut attribute if value is equal to default value on save
titlebar (Function)
- creating shortcode titlebar content using ckeditor model writer (only for block type)(arg) writer
- ckeditor model writer instance(arg) container
- ckeditor model element of parent container (place where to insert titlebar content)(arg) attributes
- shortcode attribute values(arg) parentAttributes
- parent shortcode attribute values (if shortcode has parent in schema)content (Function)
- creating wrapper for shorcode inner content using ckeditor model writer(arg) writer
- ckeditor model writer instance(arg) container
- ckeditor model element of parent container (place where to insert wrapper content)(arg) attributes
- shortcode attribute values(arg) parentAttributes
- parent shortcode attribute values (if shortcode has parent in schema)(return)
- ckeditor model element of parent container for content (place where to put shortcode inner content)preserve (Object)
- list of html tags used in wrapper for shortcode inner contentblock (Array)
- for block type(String|Object)
inline (Array)
- for inline type(String|Object)
Example #1 (block type)
window.nextgenEditor.addShortcode('fieldset', {
type: 'block',
title: 'Fieldset',
attributes: {
title: {
type: String,
title: 'Title',
widget: 'input-text',
default: '',
},
},
titlebar(writer, container, attributes) {
const strong = writer.createElement('strong');
writer.append(writer.createText(attributes.title), strong);
writer.append(writer.createText('title: '), container);
writer.append(strong, container);
},
upcast(writer, container, attributes) {
const fieldset = writer.createElement('fieldset');
writer.append(fieldset, container);
const legend = writer.createElement('legend');
writer.append(writer.createText(attributes.title), legend);
writer.append(legend, fieldset);
const content = writer.createElement('div');
writer.append(content, fieldset);
return content;
},
preserve: {
block: [
'fieldset',
{ name: 'legend', readonly: true },
],
},
});
Example #2 (inline type)
window.nextgenEditor.addShortcode('color', {
type: 'inline',
title: 'Color',
attributes: {
value: {
type: String,
title: 'Value',
widget: 'input-text',
default: '',
},
},
upcast(writer, container, attributes) {
const font = writer.createElement('font', { color: attributes.value });
writer.append(font, container);
return font;
},
preserve: {
inline: ['font'],
},
});
Credits
This plugin is built on-top of the fantastic CKEditor5 plugin