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 6 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 @@ -2,10 +2,14 @@

### 2.18

- `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.

> *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
64 changes: 44 additions & 20 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
# Editor.js API


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
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 a tool name?


`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 Tool's methods with checks and error handlers under-the-hood
Copy link
Member

Choose a reason for hiding this comment

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

what methods?


`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 @@ -36,13 +60,13 @@ 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* make Block stretched
Copy link
Member

Choose a reason for hiding this comment

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

what to use instead?


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

`insert(type?: string, data?: BlockToolData, config?: ToolConfig, index?: number, needToFocus?: boolean)` - insert new Block with passed parameters

Expand Down Expand Up @@ -100,11 +124,11 @@ Each method accept `position` and `offset` parameters. `Offset` should be used t

`Position` can be one of the following values:

| Value | Description
| --------- | -----------
| Value | Description
| --------- | -----------
| `start` | Caret will be set at the Block's beginning
| `end` | Caret will be set at the Block end
| `default` | More or less emulates browser behaviour, in most cases behaves as `start`
| `default` | More or less emulates browser behaviour, in most cases behaves as `start`

Each method returns `boolean` value: true if caret is set successfully or false otherwise (e.g. when there is no Block at index);

Expand Down Expand Up @@ -148,7 +172,7 @@ this.api.notifier.show({
![](https://capella.pics/14fcdbe4-d6eb-41d4-b66e-e0e86ccf1a4b.jpg)


Check out [`codex-notifier` package page](https://github.com/codex-team/js-notifier) on GitHub to find docs, params and examples.
Check out [`codex-notifier` package page](https://github.com/codex-team/js-notifier) on GitHub to find docs, params and examples.

### Destroy API

Expand All @@ -173,28 +197,28 @@ Methods for showing Tooltip helper near your elements. Parameters are the same a
#### Show

Method shows tooltip with custom content on passed element

```js
this.api.tooltip.show(element, content, options);
```
```

| parameter | type | description |
| -- | -- | -- |
| `element` | _HTMLElement_ | Tooltip will be showed near this element |
| `content` | _String_ or _Node_ | Content that will be appended to the Tooltip |
| `options` | _Object_ | Some displaying options, see below |

Available showing options
Available showing options

| name | type | action |
| -- | -- | -- |
| placement | `top`, `bottom`, `left`, `right` | Where to place the tooltip. Default value is `bottom' |
| marginTop | _Number_ | Offset above the tooltip with `top` placement |
| marginBottom | _Number_ | Offset below the tooltip with `bottom` placement |
| marginLeft | _Number_ | Offset at left from the tooltip with `left` placement |
| marginRight | _Number_ | Offset at right from the tooltip with `right` placement |
| delay | _Number_ | Delay before showing, in ms. Default is `70` |
| hidingDelay | _Number_ | Delay before hiding, in ms. Default is `0` |
| placement | `top`, `bottom`, `left`, `right` | Where to place the tooltip. Default value is `bottom' |
| marginTop | _Number_ | Offset above the tooltip with `top` placement |
| marginBottom | _Number_ | Offset below the tooltip with `bottom` placement |
| marginLeft | _Number_ | Offset at left from the tooltip with `left` placement |
| marginRight | _Number_ | Offset at right from the tooltip with `right` placement |
| delay | _Number_ | Delay before showing, in ms. Default is `70` |
| hidingDelay | _Number_ | Delay before hiding, in ms. Default is `0` |

#### Hide

Expand All @@ -206,15 +230,15 @@ this.api.tooltip.hide();

#### onHover

Decorator for showing tooltip near some element by "mouseenter" and hide by "mouseleave".
Decorator for showing tooltip near some element by "mouseenter" and hide by "mouseleave".

```js
this.api.tooltip.onHover(element, content, options);
```

### API Shorthands

Editor`s API provides some shorthands for API methods.
Editor`s API provides some shorthands for API methods.

| Alias | Method |
| ------ | --------------- |
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 {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
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