Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] BlockAPI Interface #1075

Merged
merged 13 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/editor.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 2.18

- `New` *I18n API* — Ability to provide internalization for Editor.js core and tools. [#751](https://github.com/codex-team/editor.js/issues/751)
- `New` — Block API that allows you to access certain Block properties and methods
- `Improvements` - TSLint (deprecated) replaced with ESLint, old config changed to [CodeX ESLint Config](https://github.com/codex-team/eslint-config).
- `Improvements` - Fix many code-style issues, add missed annotations.
- `Improvements` - Adjusted GitHub action for ESLint.
Expand All @@ -20,6 +21,9 @@
- `Fix` - Public getter `shortcut` now works for Inline Tools [#1132](https://github.com/codex-team/editor.js/issues/1132)
- `Fix` - `CMD+A` handler removed after Editor.js destroy [#1133](https://github.com/codex-team/editor.js/issues/1133)

> *Breaking changes* `blocks.getBlockByIndex` method now returns BlockAPI object. To access old value, use BlockAPI.holder property


### 2.17

- `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter
Expand Down
30 changes: 27 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,33 @@ Most actual API described by [this interface](../types/api/index.d.ts).

---

Blocks have access to the public methods provided by Editor.js API Module. Plugin and Tune Developers
Tools have access to the public methods provided by Editor.js API Module. Plugin and Tune Developers
can use Editor\`s API as they want.

## Block API

API for certain Block methods and properties. You can access it through `editor.api.block.getBlockByIndex` method or get it form `block` property of [Tool constructor](../types/tools/block-tool.d.ts) argument.

`name: string` — Block's Tool name (key, specified in `tools` property of initial configuration)

`config: ToolConfig` — Tool config passed on Editor initialization

`holder: HTMLElement` — HTML Element that wraps Tool's HTML content
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an element returned by tools render method? The description is not clear.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used this definition earlier for the same thing, is it better?
image


`isEmpty: boolean` — `true` if Block has any editable content

`selected: boolean` - `true` if Block is selected with Cross-Block Selection

`set stretched(state: boolean)` — set Block's stretch state

`stretched: boolean` — `true` if Block is stretched

`call(methodName: string, param?: object): void` — method to call any Tool's instance methods with checks and error handlers under-the-hood. For example, [Block lifecycle hooks](./tools.md#block-lifecycle-hooks)

`save(): Promise<void|SavedData>` — returns data saved from current Block's state, including Tool name and saving exec time
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • what is the difference between SavedData and BlockToolData?
  • saving exec time - we include it only for common saving, not for every block.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BlockToolData is an object, returned from save method of Tool instance. Block instance save wraps BlockToolData with SavedData object.

BlockToolData

{
  "text": "Some text"
}

SavedData

{
  "tool": "paragraph",
  "data": {
    "text": "Some text"
  },
  time: 0.1923
}

General editor.save method uses time from SavedData from each Block to calculate final exec time.

I agree that we have some inconsistency here, but I think it's out of the scope of this feature.


`validate(data: BlockToolData): Promise<boolean>` — calls Tool's validate method if exists

## Api object description

Common API interface.
Expand Down Expand Up @@ -43,11 +67,11 @@ use 'move' instead)

`getCurrentBlockIndex()` - current Block index

`getBlockByIndex(index: Number)` - returns Block with passed index
`getBlockByIndex(index: Number)` - returns Block API object by passed index

`getBlocksCount()` - returns Blocks count

`stretchBlock(index: number, status: boolean)` - make Block stretched
`stretchBlock(index: number, status: boolean)` - _Deprecated. Use Block API interface instead._ make Block stretched.

`insertNewBlock()` - __Deprecated__ insert new Block after working place

Expand Down
37 changes: 19 additions & 18 deletions docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ Each Tool should have an installation guide.

Each Tool's instance called with an params object.

| Param | Type | Description |
| ------ | ------------------- | ----------------------------------------------- |
| api | [`IAPI`][iapi-link] | Editor.js's API methods |
| config | `object` | Special configuration params passed in «config» |
| data | `object` | Data to be rendered in this Tool |
| 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

Expand Down Expand Up @@ -228,14 +229,14 @@ onPaste (event) {

### Disable paste handling

If you need to disable paste handling on your Tool for some reason, you can provide `false` as `pasteConfig` value.
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>

Expand Down Expand Up @@ -364,7 +365,7 @@ Editor.js has a Conversion Toolbar that allows user to convert one Block to anot
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.
This Toolbar will contain only Tools that specified an «import» rule.

Example:

Expand All @@ -391,11 +392,11 @@ class Header {

### Your Tool -> other Tool

The «export» field specifies how to represent your Tool's data as a string to pass it to 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.
`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:

Expand All @@ -411,7 +412,7 @@ class ListTool {
type: 'ordered'
}
}

static get conversionConfig() {
return {
export: (data) => {
Expand All @@ -425,11 +426,11 @@ class ListTool {

### Other Tool -> your Tool

The «import» rule specifies how to create your Tool's data object from the string created by original block.
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`.
It can be a `String` or a `Function`.

`String` means the key in tool data that will be filled by an exported string.
`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:
Expand All @@ -442,13 +443,13 @@ class ListTool {
type: 'unordered'
}
}

static get conversionConfig() {
return {
// ... export rule
// ... export rule

/**
* In this example, List Tool creates items by splitting original text by a dot symbol.
* In this example, List Tool creates items by splitting original text by a dot symbol.
*/
import: (string) => {
const items = string.split('.');
Expand Down
3 changes: 2 additions & 1 deletion src/components/block-tunes/block-tune-move-down.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export default class MoveDownTune implements BlockTune {
return;
}

const nextBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex + 1);
const nextBlock = this.api.blocks.getBlockByIndex(currentBlockIndex + 1);
const nextBlockElement = nextBlock.holder;
const nextBlockCoords = nextBlockElement.getBoundingClientRect();

let scrollOffset = Math.abs(window.innerHeight - nextBlockElement.offsetHeight);
Expand Down
6 changes: 4 additions & 2 deletions src/components/block-tunes/block-tune-move-up.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ export default class MoveUpTune implements BlockTune {
return;
}

const currentBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex);
const previousBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex - 1);
const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
const currentBlockElement = currentBlock.holder;
const previousBlock = this.api.blocks.getBlockByIndex(currentBlockIndex - 1);
const previousBlockElement = previousBlock.holder;

/**
* Here is two cases:
Expand Down
114 changes: 114 additions & 0 deletions src/components/block/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import Block from './index';
import { BlockToolData, ToolConfig } from '../../../types/tools';
import { SavedData } from '../../types-internal/block-data';
import { BlockAPI as BlockAPIInterface } from '../../../types/api';

/**
* Constructs new BlockAPI object
*
* @class
*
* @param {Block} block - Block to expose
*/
function BlockAPI(block: Block): void {
const blockAPI: BlockAPIInterface = {
/**
* Tool name
*
* @returns {string}
*/
get name(): string {
return block.name;
},

/**
* Tool config passed on Editor's initialization
*
* @returns {ToolConfig}
*/
get config(): ToolConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be a misunderstanding with existed Tool config term.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's exactly the ToolConfig object

return block.config;
},

/**
* .ce-block element, that wraps plugin contents
*
* @returns {HTMLElement}
*/
get holder(): HTMLElement {
return block.holder;
},

/**
* True if Block content is empty
*
* @returns {boolean}
*/
get isEmpty(): boolean {
return block.isEmpty;
},

/**
* True if Block is selected with Cross-Block selection
*
* @returns {boolean}
*/
get selected(): boolean {
return block.selected;
},

/**
* Set Block's stretch state
*
* @param {boolean} state — state to set
*/
set stretched(state: boolean) {
block.stretched = state;
},

/**
* True if Block is stretched
*
* @returns {boolean}
*/
get stretched(): boolean {
return block.stretched;
},

/**
* Call Tool method with errors handler under-the-hood
*
* @param {string} methodName - method to call
* @param {object} param - object with parameters
*
* @returns {void}
*/
call(methodName: string, param?: object): void {
block.call(methodName, param);
},

/**
* Save Block content
*
* @returns {Promise<void|SavedData>}
*/
save(): Promise<void|SavedData> {
return block.save();
},

/**
* Validate Block data
*
* @param {BlockToolData} data - data to validate
*
* @returns {Promise<boolean>}
*/
validate(data: BlockToolData): Promise<boolean> {
return block.validate(data);
},
};

Object.setPrototypeOf(this, blockAPI);
}

export default BlockAPI;
Loading