Skip to content

Commit 9f9e3f4

Browse files
Merge branch 'codex-team:next' into next
2 parents f68cfd5 + ba8fa73 commit 9f9e3f4

37 files changed

+580
-132
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,17 @@ Take a look at the [example.html](example/example.html) to view more detailed ex
116116

117117
<img align="right" width="342" src="./assets/roadmap.png" style="margin-left: 30px">
118118

119-
- Unified Toolbox
119+
- Unified Toolbars
120120
- [x] Block Tunes moved left
121121
- [x] Toolbox becomes vertical
122122
- [x] Ability to display several Toolbox buttons by the single Tool
123123
- [x] Block Tunes become vertical
124-
- [ ] Block Tunes support nested menus
125-
- [ ] Conversion Toolbar uses Unified Toolbox
126-
- [ ] Conversion Toolbar added to the Block Tunes
124+
- [x] Block Tunes support nested menus
125+
- [x] Block Tunes support separators
126+
- [x] Conversion Menu added to the Block Tunes
127+
- [x] Unified Toolbar supports hints
128+
- [x] Conversion Toolbar uses Unified Toolbar
129+
- [x] Inline Toolbar uses Unified Toolbar
127130
- Collaborative editing
128131
- [ ] Implement Inline Tools JSON format
129132
- [ ] Operations Observer, Executor, Manager, Transformer

docs/CHANGELOG.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
11
# Changelog
22

3+
### 2.30.1
4+
5+
- `Fix` – Remove fake selection after multiple "convert to" inline tool toggles
6+
37
### 2.30.0
48

59
- `New` – Block Tunes now supports nesting items
610
- `New` – Block Tunes now supports separator items
11+
- `New`*Menu Config* – New item type – HTML
12+
`New`*Menu Config* – Default and HTML items now support hints
13+
- `New` – Inline Toolbar has new look 💅
14+
- `New` – Inline Tool's `render()` now supports [Menu Config](https://editorjs.io/menu-config/) format
15+
- `New`*ToolsAPI* – All installed block tools now accessible via ToolsAPI `getBlockTools()` method
16+
- `New`*SelectionAPI* – Exposed methods `save()` and `restore()` that allow to save selection to be able to temporally move focus away, methods `setFakeBackground()` and `removeFakeBackground()` that allow to immitate selection while focus moved away
17+
- `New`*BlocksAPI* – Exposed `getBlockByElement()` method that helps find block by any child html element
718
- `New` – "Convert to" control is now also available in Block Tunes
19+
- `New` — Editor.js now supports contenteditable placeholders out of the box. Just add `data-placeholder` or `data-placeholder-active` attribute to make it work. The first one will work like native placeholder while the second one will show placeholder only when block is current.
20+
- `Improvement` — Now Paragraph placeholder will be shown for the current paragraph, not only the first one.
21+
- `Improvment` - The API `blocks.update` now accepts `tunes` data as optional third argument and makes `data` - block data as optional.
822
- `Improvement` — The ability to merge blocks of different types (if both tools provide the conversionConfig)
23+
- `Improvement` - The API `blocks.convert()` now returns the new block API
24+
- `Improvement` - The API `caret.setToBlock()` now can accept either BlockAPI or block index or block id
25+
- `Impovement`*MenuConfig*`TunesMenuConfig` type is deprecated, use the `MenuConfig` instead
26+
`Improvement`*Types*`BlockToolConstructorOptions` type improved, `block` and `config` are not optional anymore
27+
- `Improvement` - The Plus button and Block Tunes toggler are now better aligned with large line-height blocks, such as Headings
28+
- `Improvement` — Creating links on Android devices: now the mobile keyboard will have an "Enter" key for accepting the inserted link.
29+
- `Improvement` — Placeholders will stay visible on inputs focus.
30+
`Refactoring` – Switched to Vite as Cypress bundler
931
- `Fix``onChange` will be called when removing the entire text within a descendant element of a block.
1032
- `Fix` - Unexpected new line on Enter press with selected block without caret
1133
- `Fix` - Search input autofocus loosing after Block Tunes opening
@@ -14,20 +36,8 @@
1436
- `Fix` - Unwanted soft line break on Enter press after period and space (". |") on iOS devices
1537
- `Fix` - Caret lost after block conversion on mobile devices.
1638
- `Fix` - Caret lost after Backspace at the start of block when previoius block is not convertable
17-
- `Improvement` - The API `blocks.convert()` now returns the new block API
18-
- `Improvement` - The API `caret.setToBlock()` now can accept either BlockAPI or block index or block id
19-
- `New`*Menu Config* – New item type – HTML
20-
`Refactoring` – Switched to Vite as Cypress bundler
21-
`New`*Menu Config* – Default and HTML items now support hints
22-
- `New` – Inline Toolbar has new look 💅
23-
- `New` – Inline Tool's `render()` now supports [Menu Config](https://editorjs.io/menu-config/) format
24-
- `New`*ToolsAPI* – All installed block tools now accessible via ToolsAPI `getBlockTools()` method
25-
- `New`*SelectionAPI* – Exposed methods `save()` and `restore()` that allow to save selection to be able to temporally move focus away, methods `setFakeBackground()` and `removeFakeBackground()` that allow to immitate selection while focus moved away
26-
- `Impovement`*MenuConfig* – TunesMenuConfig deprecated, use MenuConfig type instead
27-
- `New`*BlocksAPI* – Exposed `getBlockByElement()` method that helps find block by any child html element
2839
`Fix` — Deleting whitespaces at the start/end of the block
29-
`Improvement`*Types*`BlockToolConstructorOptions` type improved, `block` and `config` are not optional anymore
30-
- `Improvement` - The Plus button and Block Tunes toggler are now better aligned with large line-height blocks, such as Headings
40+
- `Fix` — The problem caused by missed "import type" in block mutation event types resolved
3141

3242
### 2.29.1
3343

docs/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ use 'move' instead)
7979

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

82-
`update(id: string, data: BlockToolData)` - updates data for the block with passed id
82+
`update(id: string, data?: BlockToolData, tunes?: {[name: string]: BlockTuneData})` - updates block data and block tunes for the block with passed id
8383

8484
#### SanitizerAPI
8585

index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@
203203
*/
204204
// defaultBlock: 'paragraph',
205205

206+
placeholder: 'Write something or press / to select a tool',
207+
autofocus: true,
208+
206209
/**
207210
* Initial Editor data
208211
*/

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@editorjs/editorjs",
3-
"version": "2.30.0-rc.13",
4-
"description": "Editor.js — Native JS, based on API and Open Source",
3+
"version": "2.30.1",
4+
"description": "Editor.js — open source block-style WYSIWYG editor with JSON output",
55
"main": "dist/editorjs.umd.js",
66
"module": "dist/editorjs.mjs",
77
"types": "./types/index.d.ts",
@@ -10,7 +10,8 @@
1010
"text editor",
1111
"editor",
1212
"editor.js",
13-
"editorjs"
13+
"editorjs",
14+
"wysiwyg"
1415
],
1516
"scripts": {
1617
"dev": "vite",
@@ -45,7 +46,7 @@
4546
"@editorjs/code": "^2.7.0",
4647
"@editorjs/delimiter": "^1.2.0",
4748
"@editorjs/header": "^2.7.0",
48-
"@editorjs/paragraph": "^2.11.4",
49+
"@editorjs/paragraph": "^2.11.6",
4950
"@editorjs/simple-image": "^1.4.1",
5051
"@types/node": "^18.15.11",
5152
"chai-subset": "^1.6.0",

src/components/block/index.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '../../../types';
1111

1212
import { SavedData } from '../../../types/data-formats';
13-
import $ from '../dom';
13+
import $, { toggleEmptyMark } from '../dom';
1414
import * as _ from '../utils';
1515
import ApiModules from '../modules/api';
1616
import BlockAPI from './api';
@@ -183,11 +183,6 @@ export default class Block extends EventsDispatcher<BlockEvents> {
183183
*/
184184
private unavailableTunesData: { [name: string]: BlockTuneData } = {};
185185

186-
/**
187-
* Editor`s API module
188-
*/
189-
private readonly api: ApiModules;
190-
191186
/**
192187
* Focused input index
193188
*
@@ -223,7 +218,6 @@ export default class Block extends EventsDispatcher<BlockEvents> {
223218
id = _.generateBlockId(),
224219
data,
225220
tool,
226-
api,
227221
readOnly,
228222
tunesData,
229223
}: BlockConstructorOptions, eventBus?: EventsDispatcher<EditorEventMap>) {
@@ -232,7 +226,6 @@ export default class Block extends EventsDispatcher<BlockEvents> {
232226
this.id = id;
233227
this.settings = tool.settings;
234228
this.config = tool.settings.config || {};
235-
this.api = api;
236229
this.editorEventBus = eventBus || null;
237230
this.blockAPI = new BlockAPI(this);
238231

@@ -262,6 +255,12 @@ export default class Block extends EventsDispatcher<BlockEvents> {
262255
* so we need to track focus events to update current input and clear cache.
263256
*/
264257
this.addInputEvents();
258+
259+
/**
260+
* We mark inputs with [data-empty] attribute
261+
* It can be useful for developers, for example for correct placeholder behavior
262+
*/
263+
this.toggleInputsEmptyMark();
265264
});
266265
}
267266

@@ -938,6 +937,11 @@ export default class Block extends EventsDispatcher<BlockEvents> {
938937
*/
939938
this.updateCurrentInput();
940939

940+
/**
941+
* We mark inputs with 'data-empty' attribute, so new inputs should be marked as well
942+
*/
943+
this.toggleInputsEmptyMark();
944+
941945
this.call(BlockToolAPI.UPDATED);
942946

943947
/**
@@ -1000,4 +1004,11 @@ export default class Block extends EventsDispatcher<BlockEvents> {
10001004
private dropInputsCache(): void {
10011005
this.cachedInputs = [];
10021006
}
1007+
1008+
/**
1009+
* Mark inputs with 'data-empty' attribute with the empty state
1010+
*/
1011+
private toggleInputsEmptyMark(): void {
1012+
this.inputs.forEach(toggleEmptyMark);
1013+
}
10031014
}

src/components/dom.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,3 +662,13 @@ export function calculateBaseline(element: Element): number {
662662

663663
return baselineY;
664664
}
665+
666+
/**
667+
* Toggles the [data-empty] attribute on element depending on its emptiness
668+
* Used to mark empty inputs with a special attribute for placeholders feature
669+
*
670+
* @param element - The element to toggle the [data-empty] attribute on
671+
*/
672+
export function toggleEmptyMark(element: HTMLElement): void {
673+
element.dataset.empty = Dom.isEmpty(element) ? 'true' : 'false';
674+
}

src/components/inline-tools/inline-tool-convert.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IconReplace } from '@codexteam/icons';
22
import { InlineTool, API } from '../../../types';
3-
import { MenuConfig } from '../../../types/tools';
3+
import { MenuConfig, MenuConfigItem } from '../../../types/tools';
44
import * as _ from '../utils';
55
import { Blocks, Selection, Tools, I18n, Caret } from '../../../types/api';
66
import SelectionUtils from '../selection';
@@ -57,15 +57,20 @@ export default class ConvertInlineTool implements InlineTool {
5757
public async render(): Promise<MenuConfig> {
5858
const currentSelection = SelectionUtils.get();
5959
const currentBlock = this.blocksAPI.getBlockByElement(currentSelection.anchorNode as HTMLElement);
60+
61+
if (currentBlock === undefined) {
62+
return [];
63+
}
64+
6065
const allBlockTools = this.toolsAPI.getBlockTools();
6166
const convertibleTools = await getConvertibleToolsForBlock(currentBlock, allBlockTools);
6267

6368
if (convertibleTools.length === 0) {
6469
return [];
6570
}
6671

67-
const convertToItems = convertibleTools.reduce((result, tool) => {
68-
tool.toolbox.forEach((toolboxItem) => {
72+
const convertToItems = convertibleTools.reduce<MenuConfigItem[]>((result, tool) => {
73+
tool.toolbox?.forEach((toolboxItem) => {
6974
result.push({
7075
icon: toolboxItem.icon,
7176
title: toolboxItem.title,

src/components/inline-tools/inline-tool-link.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export default class LinkInlineTool implements InlineTool {
134134
public renderActions(): HTMLElement {
135135
this.nodes.input = document.createElement('input') as HTMLInputElement;
136136
this.nodes.input.placeholder = this.i18n.t('Add a link');
137+
this.nodes.input.enterKeyHint = 'done';
137138
this.nodes.input.classList.add(this.CSS.input);
138139
this.nodes.input.addEventListener('keydown', (event: KeyboardEvent) => {
139140
if (event.keyCode === this.ENTER_KEY) {

src/components/modules/api/blocks.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import * as _ from './../../utils';
44
import BlockAPI from '../../block/api';
55
import Module from '../../__module';
66
import Block from '../../block';
7-
import { capitalize } from './../../utils';
7+
import { capitalize } from '../../utils';
8+
import { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
89

910
/**
1011
* @class BlocksAPI
@@ -320,17 +321,18 @@ export default class BlocksAPI extends Module {
320321
* Updates block data by id
321322
*
322323
* @param id - id of the block to update
323-
* @param data - the new data
324+
* @param data - (optional) the new data
325+
* @param tunes - (optional) tune data
324326
*/
325-
public update = async (id: string, data: Partial<BlockToolData>): Promise<BlockAPIInterface> => {
327+
public update = async (id: string, data?: Partial<BlockToolData>, tunes?: {[name: string]: BlockTuneData}): Promise<BlockAPIInterface> => {
326328
const { BlockManager } = this.Editor;
327329
const block = BlockManager.getBlockById(id);
328330

329331
if (block === undefined) {
330332
throw new Error(`Block with id "${id}" not found`);
331333
}
332334

333-
const updatedBlock = await BlockManager.update(block, data);
335+
const updatedBlock = await BlockManager.update(block, data, tunes);
334336

335337
// we cast to any because our BlockAPI has no "new" signature
336338
// eslint-disable-next-line @typescript-eslint/no-explicit-any

0 commit comments

Comments
 (0)