@automattic/isolated-block-editor

Repackages Gutenberg's editor playground as multi-instance editor.

Usage no npm install needed!

<script type="module">
  import automatticIsolatedBlockEditor from 'https://cdn.skypack.dev/@automattic/isolated-block-editor';
</script>

README

Isolated Block Editor

Repackages Gutenberg's editor playground as a full-featured multi-instance editor that does not require WordPress.

The key features are:

  • Extends the Gutenberg playground editor to match a full editor
  • Allows multiple onscreen instances with seperate data stores and keyboard handlers
  • Undo history

And a full list of features:

  • Dynamic switching of data stores for compatability with third-party blocks
  • Works at sizes smaller than full screen
  • Block allow and disallow list, per instance
  • Preferences (saved to localStorage) and options (saved to memory), per instance
  • Overriding of key Gutenberg store functions
  • Patterns, reusable blocks, groups, and template support
  • Block inserter in a popover
  • Block inspector in a popover
  • Built in toolbar with options for displaying a more menu, block inspector, and block buttons
  • Fullscreen mode (requires additional CSS)
  • Preview mode (requires additional CSS and JS)
  • Visual & code editor
  • PHP (WordPress) code to load the editor outside of wp-admin
  • Menu for custom links
  • Re-routing of WordPress API requests

The Isolated Block Editor is provided in three forms:

  • ES6 module
  • CommonJS module
  • Standalone JavaScript file, for inclusion on any browser page

Requires: Gutenberg 10.6.0, tested up to Gutenberg 10.9.0 RC 1

Examples:

  • Plain Text Editor - standalone JS and CSS file that can replace any textarea on any page with a full Gutenberg editor
  • Gutenberg Everywhere - a WordPress plugin to add Gutenberg to comments, WP admin pages, bbPress, and BuddyPress
  • Gutenberg Chrome Extension - a Chrome extension that allows Gutenberg to be used on any page
  • Gutenberg Desktop - a desktop editor that supports the loading and saving of HTML and Markdown files
  • P2 - WordPress as a collaborative workspace (coming soon for self-hosted)
  • Editor Block - a block that allows an editor to be added to a page (coming soon)

Do you use this in your own project? Let us know!

Why would I use this?

Gutenberg already provides a playground which allows it to be used outside of WordPress. This is actually used as the basis for the Isolated Block Editor.

However, it provides a limited set of features, and extending it further into a usable editor can take some effort. For example, it doesn't include any undo history.

The Isolated Block Editor is a full editor that handles a lot of these complexities. It should not be considered a replacement for the Gutenberg playground, but more of an opinionated layer on top.

This should be considered experimental, and subject to changes.

Bundling and WordPress

The Isolated Block Editor aims to provide an editor that is as isolated from WordPress as possible. However, it can still be used on a WordPress site, and the decision comes down to the bundling:

  • Bundled without Gutenberg - if used on a WordPress site then you can use the Gutenberg code already included with WordPress. You will need a working WordPress site, but do not need to include Gutenberg within your editor
  • Bundled with Gutenberg - Gutenberg is included within the editor and there is no dependency on WordPress or PHP

Examples are provided for both situations (see Plain text editor for bundled and Gutenberg Everywhere for unbundled).

They key difference is in the Webpack config. If you don't want to bundle Gutenberg with your editor then you can use the DependencyExtractionWebpackPlugin plugin:

plugins: [
    new DependencyExtractionWebpackPlugin( { injectPolyfill: true } )
]

Alternatively you can use the @wordpress/scripts build system, which automatically runs DependencyExtractionWebpackPlugin:

wp-scripts start

You can use the provided iso-gutenberg.php file to help when using the IsolatedBlockEditor on a WordPress site. It will load Gutenberg and set up your configuration.

Standalone Module

You can use the provided isolated-block-editor.js, core.css, and isolated-block-editor.css files on any web page, regardless of whether it is on WordPress. It will provide two global functions which can turn a textarea into a block editor. Here's an example:

<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<script src="isolated-block-editor.js"></script>
<link rel="stylesheet" href="core.css" />
<link rel="stylesheet" href="isolated-block-editor.css" />

<body>
    <textarea id="editor" />

    <script>
        wp.attachEditor( document.getElementById( 'editor' ) );
    </script>
</body>

Note that you must load React before loading the editor.

You can find an example in src/browser/index.html.

CSS

If you are using on a WordPress site then WordPress will load the core Gutenberg CSS as part of the iso-gutenberg.php script.

If you are not using on a WordPress site then you will need to load the core CSS yourself. You can either do this by including the following modules and importing directly:

  • @wordpress/components
  • @wordpress/block-editor
  • @wordpress/block-library
  • @wordpress/format-library
@import '@wordpress/components/build-style/style.css';
@import '@wordpress/block-editor/build-style/style.css';
@import '@wordpress/block-library/build-style/style.css';
@import '@wordpress/block-library/build-style/editor.css';
@import '@wordpress/block-library/build-style/theme.css';
@import '@wordpress/format-library/build-style/style.css';

Alternatively you can directly import the bundled build-browser/core.css CSS:

import 'isolated-block-editor/build-browser/core.css';

Using

The module is currently only available on Github and can be added with:

npm install @automattic/isolated-block-editor@1.2.0" --save (where 1.2.0 is the version you want to use)

Future

The code here deals with two kinds of problems:

  • Adding features to the Gutenberg playground - these are already provided by Gutenberg, but are packaged up here for you
  • Adding workarounds to Gutenberg - these are situations where Gutenberg doesn't provide a feature, or doesn't export a feature, and so a workaround is needed.

It is hoped that most of the workarounds can be migrated back to Gutenberg so that they are no longer needed. Sometimes these workarounds involve duplicating parts of Gutenberg, which is not ideal. It is possible that the Isolated Block Editor may no longer be needed as a seperate entity.

Development

If multiple editors are on-screen then the IsolatedBlockEditor will ensure that the wp global refers to the currently focussed instance. This should make it more compatible with plugins and third-party code that uses the wp global.

Usage

Include the IsolatedBlockEditor module and then create an instance:


import IsolatedBlockEditor from 'isolated-block-editor';

render(
    <IsolatedBlockEditor
        settings={ settings }
        onSaveContent={ ( html ) => saveContent( html ) }
        onLoad={ ( parse ) => loadInitialContent( parse ) }
        onError={ () => document.location.reload() }
    >
    </IsolatedBlockEditor>,
    document.querySelector( '#editor' )
);

The IsolatedBlockEditor also exports the following support components:

  • EditorLoaded - Include this to be notified when the editor is loading and has loaded
  • DocumentSection - Wrap up a component to appear in the document tab of the inspector
  • ToolbarSlot - Insert content into the toolbar
  • FooterSlot - Insert content into the footer
  • CollaborativeEditing - Enable real-time collaborative editing

import IsolatedBlockEditor, { EditorLoaded, DocumentSection, ToolbarSlot, CollaborativeEditing } from 'isolated-block-editor';

render(
    <IsolatedBlockEditor
        settings={ settings }
        onSaveContent={ ( html ) => saveContent( html ) }
        onLoad={ ( parse ) => loadInitialContent( parse ) }
        onError={ () => document.location.reload() }
    >
        <EditorLoaded onLoaded={ () => {} } onLoading={ () => {} } />
        <DocumentSection>Extra Information</DocumentSection>
        <CollaborativeEditing settings={ collabSettings } />

        <ToolbarSlot>
            <button>Beep!</button>
        </ToolbarSlot>
    </IsolatedBlockEditor>,
    document.querySelector( '#editor' )
);

The following function is also provided:

  • initializeEditor - Call this to initialize the editor if it needs to be done before being used.

Props

settings

  • iso [object] - IsolatedBlockEditor settings object

  • iso.preferencesKey [string|null] - Preferences key. Default to null to disable

  • iso.defaultPreferences {object} - Default preferences

  • iso.persistenceKey [string|null] - Persistence key. Default to null to disable

  • iso.customStores [array] - Array of store objects, in a form suitable for passing to Gutenberg's createReduxStore

  • iso.blocks [object] - Block restrictions

  • iso.blocks.allowBlocks [string[]] - list of block names to allow, defaults to none

  • iso.blocks.disallowBlocks [string[]] - list of block names to disallow, defaults to none

  • iso.disallowEmbed [string[]] - List of embed names to remove, defaults to none.

  • iso.toolbar [Object] - Toolbar settings

  • iso.toolbar.inserter [boolean] - Enable or disable the toolbar block inserter, defaults to true

  • iso.toolbar.inspector [boolean] - Enable or disable the toolbar block inspector, defaults to false

  • iso.toolbar.navigation [boolean] - Enable or disable the toolbar navigation button, defaults to false

  • iso.toolbar.toc [boolean] - Enable or disable the toolbar table of contents button, defaults to false

  • iso.toolbar.undo [boolean] - Enable or disable the toolbar undo/redo buttons, defaults to true

  • iso.toolbar.documentInspector [string|null] - Set to a string to show as a new tab in the inspector and filled with DocumentSection, otherwise defaults to no new tab

  • iso.moreMenu [Object] - More menu settings

  • iso.moreMenu.editor [boolean] - Enable or disable the editor sub menu (visual/code editing), defaults to false

  • iso.moreMenu.fullscreen [boolean] - Enable or disable the fullscreen option, defaults to false

  • iso.moreMenu.preview [boolean] - Enable or disable the preview option, defaults to false

  • iso.moreMenu.topToolbar [boolean] - Enable or disable the 'top toolbar' option, defaults to false

  • iso.linkMenu [array] - Link menu settings. Array of title and url, defaults to none

  • iso.currentPattern [string] - The pattern to start with, defaults to none

  • iso.allowApi [boolean] - Allow API requests, defaults to false

  • editor [object] - Gutenberg settings object

A settings object that contains all settings for the IsolatedBlockEditor, as well as for Gutenberg. Any settings not provided will use defaults.

The block allow and disallow list works as follows:

  • All blocks are allowed, unless the allowBlocks option is set which defines the list of allowed blocks
  • Anything in the disallowBlocks list is removed from the list of allowed blocks.

onSaveContent

  • content - content to be saved, as an HTML string

Save callback that saves the content as an HTML string. Will be called for each change in the editor content.

onSaveBlocks

  • blocks [Array] - blocks to be saved
  • ignoredContent [Array]- array of HTML strings of content that can be ignored

Save callback that is supplied with a list of blocks and a list of ignored content. This gives more control than onSaveContent, and is used if you want to filter the saved blocks. For example, if you are using a template then it will appear in the ignoredContent, and you can then ignore the onSaveBlocks call if it matches the blocks.

onLoad

  • parse [string] - Gutenberg parse function that parses HTML as a string and returns blocks

Load the initial content into the editor. This is a callback that runs after the editor has been created, and is supplied with a parse function that is specific to this editor instance. This should be used so that the appropriate blocks are available.

onError

Callback if an error occurs.

className

Additional class name attached to the editor.

renderMoreMenu

  • menuSettings [object] - settings for this particular editor
  • onClose [func] - Callback to close the more menu

Render additional components in the more menu.

Note: this needs improving or replacing with something more flexible

children

Add any components to customise the editor. These components will have access to the editor's Redux store.

Extending

Custom behaviour can be added through child components. These components will have access to the isolated/editor store, as well as to the editor instance versions of core/block-editor.

Gutenberg requirements

Gutenberg uses iframes to display various parts of the editor, and it uses the global window.__editorAssets to store styles and scripts that will be added to this iframe. You may need to also use this.

The default is:

window.__editorAssets = { styles: '', scripts: '' }

The values should be full <link> and <script> tags.

Releasing

To make a release, ensure you are on the trunk branch. Do not update the version number in package.json - the release process will do this for you. Then run:

GITHUB_TOKEN=<TOKEN> yarn dist