editor.js/docs/tools.md
Mihir Rane 49516dd14a
Rename initialBlock to defaultBlock (#1209)
* [Improvements] ESLint action (#1099)

* TSLint -> ESLint, GitHub Action

* Update eslint.yml

* Autofix

* more autofix

* fix

* manually fix some issues

* Update CHANGELOG.md

* [Refactor] ESLint fixed (#1100)

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* [Feature] i18n (#1106)

* i18n first steps

* i18n internal, toolbox, api for tools

* namespaced api

* tn, t

* tn in block tunes

* join toolbox and inlineTools under toolNames

* translations

* make enum toolTypes

* Update block.ts

* Update src/components/core.ts

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* add more types

* rm tn

* export i18n types

* upd bundle

* fix tabulation

* Add type-safe namespaces

* upd

* Improve example

* Update toolbox.ts

* improve examplle

* upd

* fix typo

* Add comments for complex types

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>

* Remove unused submodule

* Fixed: icon centering in Firefox

* Do not load styles twice (#1112)

* Do not load styles twice

* Add changelog

* Fix issue link

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Show warning if Block to delete is not found (#1111)

Resolves #1102

* Save Tools' order in the Toolbox (#1113)

Resolves #1073

* fix $.isEmpty performance (#1096)

* fix $.isEmpty performance

* add changelog

* upd bundle

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Add issue templates (#1114)

* Update issue templates (#1121)

* Update issue templates

* Apply suggestions from code review

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

* upd texts

* Update feature_request.md

* Update .github/ISSUE_TEMPLATE/discussion.md

Co-Authored-By: George Berezhnoy <gohabereg@users.noreply.github.com>

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>

* Allowing deleting block by block id (#1108)

* Allowing deleting block by block id

* Fixed no argument error

* Making index value optional for delete operation

* Added to changelog

* Making index value optional for delete operation

* Added parameter description

* Update docs/CHANGELOG.md

* Update types/api/blocks.d.ts

* Update editor.js

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Allow navigate next from last non-initial block (#1110)

Resolves #1103

* Create CODE_OF_CONDUCT.md (#1171)

* Create CODE_OF_CONDUCT.md

* Update changelog file

* Update dependencies (#1122)

* Update dependencies

* upd codex.tooltip

* Update editor.js.LICENSE.txt

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Feature/disable tab event config (#1164)

* Highlight first block on autofocus (#1127)

* Fix shortcut for external tools (#1141)

* fix/shortcut-for-external-tools

* Check inline tools property for shortcut

Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* Hotfix/issue1133 selection shortcut removed on editor destroy (#1140)

* Removed shortcut CMD+A on editor destroy #1133

* Removed patch version and made code cleaner #1133

* lint error fixes #1133

Co-authored-by: Sisir <sisir@hellosivi.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>

* [Feature] BlockAPI Interface (#1075)

* Fix BlockManager.insert method (#1172)

* Fix BlockManager.insert method

* upd

* Explicitly check for undefined

* Update tools master branches (#1180)

* Update master branches

* Update image

* Update CHANGELOG.md

* Fix behaviour of inputs editing in block settings (#1123)

* lint code

* Update CHANGELOG.md

* Rename initialBlock to defaultBlock
Closes #993

The initialBlock property is renamed to defaultBlock.

* Change keyword 'InitialBlock' to 'DefaultBlock' in all methods
Fixes #993

All the methods using the keyword 'Initial' or 'initial' for initial block
are replace with 'Default' or 'default'.
For example, the Tools.isIntitial() method is changed to Tools.isDefault().

* Keep initialBlock and defaultBlock both.

initialBlock property is still kept but it will deprecated in the
next major release.

* Change defaultBlock in example.html and rebuild.

* Remove package-lock.json file.

* Update docs/tools.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example-dev.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example-dev.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update example/example.html

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update types/configs/editor-config.d.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update types/configs/editor-config.d.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Update src/components/utils.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>

* Fix needAddDefaultBlock to needToAddDefaultBlock

* Add as an Improvement to CHANGELOG.md

* Delete editor.js.map

* fix log, rename some more places

* Update example.html

* Update blockManager.ts

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: Georgy Berezhnoy <gohabereg@gmail.com>
Co-authored-by: tasuku-s <tasuku@freemind.co.jp>
Co-authored-by: Athul Anil Kumar <athul7744@outlook.com>
Co-authored-by: Taly <vitalik7tv@yandex.ru>
Co-authored-by: flaming-cl <51183663+flaming-cl@users.noreply.github.com>
Co-authored-by: Nguyen Ngoc Son <sonnn.se@gmail.com>
Co-authored-by: Sisir Das K <37764463+sis-dk@users.noreply.github.com>
Co-authored-by: Sisir <sisir@hellosivi.com>
2020-08-18 19:53:44 +03:00

484 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Editor.js Tools
Editor.js is a block-oriented editor. It means that entry composed with the list of `Blocks` of different types: `Texts`, `Headers`, `Images`, `Quotes` etc.
`Tool` — is a class that provide custom `Block` type. All Tools represented by `Plugins`.
Each Tool should have an installation guide.
## Tool class structure
### constructor()
Each Tool's instance called with an params object.
| Param | Type | Description |
| ------ | ------------------------------------------------------ | ----------------------------------------------- |
| api | [`IAPI`](../types/index.d.ts) | Editor.js's API methods |
| config | [`ToolConfig`](../types/tools/tool-config.d.ts) | Special configuration params passed in «config» |
| data | [`BlockToolData`](../types/tools/block-tool-data.d.ts) | Data to be rendered in this Tool |
| block | [`BlockAPI`](../types/api/block.d.ts) | Block's API methods |
[iapi-link]: ../src/types-internal/api.ts
#### Example
```javascript
constructor({data, config, api}) {
this.data = data;
this.api = api;
this.config = config;
// ...
}
```
### render()
Method that returns Tool's element {HTMLElement} that will be placed into Editor.
### save()
Process Tool's element created by `render()` function in DOM and return Block's data.
### validate(data: BlockToolData): boolean|Promise\<boolean\> _optional_
Allows to check correctness of Tool's data. If data didn't pass the validation it won't be saved. Receives Tool's `data` as input param and returns `boolean` result of validation.
### merge() _optional_
Method that specifies how to merge two `Blocks` of the same type, for example on `Backspace` keypress.
Method does accept data object in same format as the `Render` and it should provide logic how to combine new
data with the currently stored value.
## Internal Tool Settings
Options that Tool can specify. All settings should be passed as static properties of Tool's class.
| Name | Type | Default Value | Description |
| -- | -- | -- | -- |
| `toolbox` | _Object_ | `undefined` | Pass here `icon` and `title` to display this `Tool` in the Editor's `Toolbox` <br /> `icon` - HTML string with icon for Toolbox <br /> `title` - optional title to display in Toolbox |
| `enableLineBreaks` | _Boolean_ | `false` | With this option, Editor.js won't handle Enter keydowns. Can be helpful for Tools like `<code>` where line breaks should be handled by default behaviour. |
| `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) |
| `sanitize` | _Object_ | `undefined` | Config for automatic sanitizing of saved data. See [Sanitize](#sanitize) section. |
| `conversionConfig` | _Object_ | `undefined` | Config allows Tool to specify how it can be converted into/from another Tool. See [Conversion config](#conversion-config) section. |
## User configuration
All Tools can be configured by users. You can set up some of available settings along with Tool's class
to the `tools` property of Editor Config.
```javascript
var editor = new EditorJS({
holderId : 'editorjs',
tools: {
text: {
class: Text,
inlineToolbar : true,
// other settings..
},
header: Header
},
defaultBlock : 'text',
});
```
There are few options available by Editor.js.
| Name | Type | Default Value | Description |
| -- | -- | -- | -- |
| `inlineToolbar` | _Boolean/Array_ | `false` | Pass `true` to enable the Inline Toolbar with all Tools, or pass an array with specified Tools list |
| `config` | _Object_ | `null` | User's configuration for Plugin.
## Paste handling
Editor.js handles paste on Blocks and provides API for Tools to process the pasted data.
When user pastes content into Editor, pasted content will be splitted into blocks.
1. If plain text will be pasted, it will be splitted by new line characters
2. If HTML string will be pasted, it will be splitted by block tags
Also Editor API allows you to define your own pasting scenario. You can either:
1. Specify **HTML tags**, that can be represented by your Tool. For example, Image Tool can handle `<img>` tags.
If tags you specified will be found on content pasting, your Tool will be rendered.
2. Specify **RegExp** for pasted strings. If pattern has been matched, your Tool will be rendered.
3. Specify **MIME type** or **extensions** of files that can be handled by your Tool on pasting by drag-n-drop or from clipboard.
For each scenario, you should do 2 next things:
1. Define static getter `pasteConfig` in Tool class. Specify handled patterns there.
2. Define public method `onPaste` that will handle PasteEvent to process pasted data.
### HTML tags handling
To handle pasted HTML elements object returned from `pasteConfig` getter should contain following field:
| Name | Type | Description |
| -- | -- | -- |
| `tags` | `String[]` | _Optional_. Should contain all tag names you want to be extracted from pasted data and processed by your `onPaste` method |
For correct work you MUST provide `onPaste` handler at least for `defaultBlock` Tool.
> Example
Header Tool can handle `H1`-`H6` tags using paste handling API
```javascript
static get pasteConfig() {
return {
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'],
}
}
```
> Same tag can be handled by one (first specified) Tool only.
### RegExp patterns handling
Your Tool can analyze text by RegExp patterns to substitute pasted string with data you want. Object returned from `pasteConfig` getter should contain following field to use patterns:
| Name | Type | Description |
| -- | -- | -- |
| `patterns` | `Object` | _Optional_. `patterns` object contains RegExp patterns with their names as object's keys |
**Note** Editor will check pattern's full match, so don't forget to handle all available chars in there.
Pattern will be processed only if paste was on `defaultBlock` Tool and pasted string length is less than 450 characters.
> Example
You can handle YouTube links and insert embeded video instead:
```javascript
static get pasteConfig() {
return {
patterns: {
youtube: /http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?/
},
}
}
```
### Files pasting
Your Tool can handle files pasted or dropped into the Editor.
To handle file you should provide `files` property in your `pasteConfig` configuration object.
`files` property is an object with the following fields:
| Name | Type | Description |
| ---- | ---- | ----------- |
| `extensions` | `string[]` | _Optional_ Array of extensions your Tool can handle |
| `mimeTypes` | `sring[]` | _Optional_ Array of MIME types your Tool can handle |
Example
```javascript
static get pasteConfig() {
return {
files: {
mimeTypes: ['image/png'],
extensions: ['json']
}
}
}
```
### Pasted data handling
If you registered some paste substitutions in `pasteConfig` property, you **should** provide `onPaste` callback in your Tool class.
`onPaste` should be public non-static method. It accepts custom _PasteEvent_ object as argument.
PasteEvent is an alias for three types of events - `tag`, `pattern` and `file`. You can get the type from _PasteEvent_ object's `type` property.
Each of these events provide `detail` property with info about pasted content.
| Type | Detail |
| ----- | ------ |
| `tag` | `data` - pasted HTML element |
| `pattern` | `key` - matched pattern key you specified in `pasteConfig` object <br /> `data` - pasted string |
| `file` | `file` - pasted file |
Example
```javascript
onPaste (event) {
switch (event.type) {
case 'tag':
const element = event.detail.data;
this.handleHTMLPaste(element);
break;
case 'pattern':
const text = event.detail.data;
const key = event.detail.key;
this.handlePatternPaste(key, text);
break;
case 'file':
const file = event.detail.file;
this.handleFilePaste(file);
break;
}
}
```
### Disable paste handling
If you need to disable paste handling on your Tool for some reason, you can provide `false` as `pasteConfig` value.
That way paste event won't be processed if fired on your Tool:
```javascript
static get pasteConfig {
return false;
}
```
## Sanitize <a name="sanitize"></a>
Editor.js provides [API](sanitizer.md) to clean taint strings.
Use it manually at the `save()` method or or pass `sanitizer` config to do it automatically.
### Sanitizer Configuration
The example of sanitizer configuration
```javascript
let sanitizerConfig = {
b: true, // leave <b>
p: true, // leave <p>
}
```
Keys of config object is tags and the values is a rules.
#### Rule
Rule can be boolean, object or function. Object is a dictionary of rules for tag's attributes.
You can set `true`, to allow tag with all attributes or `false|{}` to remove all attributes,
but leave tag.
Also you can pass special attributes that you want to leave.
```javascript
a: {
href: true
}
```
If you want to use a custom handler, use should specify a function
that returns a rule.
```javascript
b: function(el) {
return !el.textContent.includes('bad text');
}
```
or
```javascript
a: function(el) {
let anchorHref = el.getAttribute('href');
if (anchorHref && anchorHref.substring(0, 4) === 'http') {
return {
href: true,
target: '_blank'
}
} else {
return {
href: true
}
}
}
```
### Manual sanitize
Call API method `sanitizer.clean()` at the save method for each field in returned data.
```javascript
save() {
return {
text: this.api.sanitizer.clean(taintString, sanitizerConfig)
}
}
```
### Automatic sanitize
If you pass the sanitizer config as static getter, Editor.js will automatically sanitize your saved data.
Note that if your Tool is allowed to use the Inline Toolbar, we will get sanitizing rules for each Inline Tool
and merge with your passed config.
You can define rules for each field
```javascript
static get sanitize() {
return {
text: {},
items: {
b: true, // leave <b>
a: false, // remove <a>
}
}
}
```
Don't forget to set the rule for each embedded subitems otherwise they will
not be sanitized.
if you want to sanitize everything and get data without any tags, use `{}` or just
ignore field in case if you want to get pure HTML
```javascript
static get sanitize() {
return {
text: {},
items: {}, // this rules will be used for all properties of this object
// or
items: {
// other objects here won't be sanitized
subitems: {
// leave <a> and <b> in subitems
a: true,
b: true,
}
}
}
}
```
## Conversion config <a name="conversion-config"></a>
Editor.js has a Conversion Toolbar that allows user to convert one Block to another.
![](https://capella.pics/6c1f708b-a30c-4ffd-a427-5b59a1a472e0.jpg)
1. You can add ability to your Tool to be converted. Specify «export» property of `conversionConfig`.
2. You can add ability to convert other Tools to your Tool. Specify «import» property of `conversionConfig`.
Conversion Toolbar will be shown only near Blocks that specified an «export» rule, when user selected almost all block's content.
This Toolbar will contain only Tools that specified an «import» rule.
Example:
```js
class Header {
constructor(){
this.data = {
text: '',
level: 2
}
}
/**
* Rules specified how our Tool can be converted to/from other Tool.
*/
static get conversionConfig() {
return {
export: 'text', // this property of tool data will be used as string to pass to other tool
import: 'text' // to this property imported string will be passed
};
}
}
```
### Your Tool -> other Tool
The «export» field specifies how to represent your Tool's data as a string to pass it to other tool.
It can be a `String` or a `Function`.
`String` means a key of your Tool data object that should be used as string to export.
`Function` is a method that accepts your Tool data and compose a string to export from it. See example below:
```js
class ListTool {
constructor(){
this.data = {
items: [
'Fisrt item',
'Second item',
'Third item'
],
type: 'ordered'
}
}
static get conversionConfig() {
return {
export: (data) => {
return data.items.join('.'); // in this example, all list items will be concatenated to an export string
},
// ... import rule
};
}
}
```
### Other Tool -> your Tool
The «import» rule specifies how to create your Tool's data object from the string created by original block.
It can be a `String` or a `Function`.
`String` means the key in tool data that will be filled by an exported string.
For example, `import: 'text'` means that `constructor` of your block will accept a `data` object with `text` property filled with string composed by original block.
`Function` allows you to specify own logic, how a string should be converted to your tool data. For example:
```js
class ListTool {
constructor(data){
this.data = data || {
items: [],
type: 'unordered'
}
}
static get conversionConfig() {
return {
// ... export rule
/**
* In this example, List Tool creates items by splitting original text by a dot symbol.
*/
import: (string) => {
const items = string.split('.');
return {
items: items.filter( (text) => text.trim() !== ''),
type: 'unordered'
};
}
};
}
}
```
## Block Lifecycle hooks
### `rendered()`
Called after Block contents is added to the page
### `updated()`
Called each time Block contents is updated
### `removed()`
Called after Block contents is removed from the page but before Block instance deleted
### `moved(MoveEvent)`
Called after Block was moved. `MoveEvent` contains `fromIndex` and `toIndex`
respectively.